diff options
4 files changed, 17 insertions, 112 deletions
diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle index 554b513..472d678 100644 --- a/android-app/app/build.gradle +++ b/android-app/app/build.gradle @@ -39,6 +39,7 @@ android { buildFeatures { viewBinding true + buildConfig true } testOptions { diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt index b4a0a91..0115a62 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/SavedTracksFragment.kt @@ -1,6 +1,5 @@ package org.terst.nav.track -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -12,7 +11,6 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.button.MaterialButton import kotlinx.coroutines.launch import org.terst.nav.MainActivity import org.terst.nav.R @@ -34,10 +32,8 @@ class SavedTracksFragment : Fragment() { (requireActivity() as MainActivity).hideOverlays() } - val rv = view.findViewById<RecyclerView>(R.id.rv_saved_tracks) - val layoutEmpty = view.findViewById<View>(R.id.layout_empty) - val tvEmpty = view.findViewById<TextView>(R.id.tv_empty) - val btnSetup = view.findViewById<MaterialButton>(R.id.btn_setup_storage) + val rv = view.findViewById<RecyclerView>(R.id.rv_saved_tracks) + val tvEmpty = view.findViewById<TextView>(R.id.tv_empty) rv.layoutManager = LinearLayoutManager(requireContext()) rv.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) @@ -54,32 +50,8 @@ class SavedTracksFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewModel.savedTracks.collect { tracks -> adapter.submitList(tracks) - val empty = tracks.isEmpty() - layoutEmpty.visibility = if (empty) View.VISIBLE else View.GONE - rv.visibility = if (empty) View.GONE else View.VISIBLE - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - viewLifecycleOwner.lifecycleScope.launch { - viewModel.safState.collect { state -> - when (state) { - SafState.NOT_CONFIGURED -> { - tvEmpty.text = "No saved tracks yet.\nRecord a trip to see it here." - btnSetup.text = "Set up track storage" - btnSetup.visibility = View.VISIBLE - } - SafState.PERMISSION_LOST -> { - tvEmpty.text = "Track storage needs re-authorization." - btnSetup.text = "Restore track access" - btnSetup.visibility = View.VISIBLE - } - SafState.CONFIGURED -> btnSetup.visibility = View.GONE - } - } - } - btnSetup.setOnClickListener { - (requireActivity() as MainActivity).launchSafPicker() + tvEmpty.visibility = if (tracks.isEmpty()) View.VISIBLE else View.GONE + rv.visibility = if (tracks.isEmpty()) View.GONE else View.VISIBLE } } } diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt index 59d7e53..146d10f 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt @@ -283,7 +283,7 @@ class TrackStorage(private val context: Context) { null ) { path, uri -> NavLogger.i("track", "triggerNavDirRescan: done path=$path uri=$uri") - if (cont.isActive) cont.resume(Unit) + if (cont.isActive) cont.resume(Unit) {} } } } ?: NavLogger.e("track", "triggerNavDirRescan: timed out") diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt index 116a90c..193dbff 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/voicelog/VoiceLogFragment.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.content.pm.PackageManager import android.graphics.Bitmap import android.net.Uri -import android.os.Build import android.os.Bundle import android.speech.RecognitionListener import android.speech.RecognizerIntent @@ -19,35 +18,24 @@ import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.textfield.TextInputEditText import kotlinx.coroutines.launch -import org.terst.nav.MainActivity import org.terst.nav.R import org.terst.nav.logbook.VoiceLogState import org.terst.nav.logbook.VoiceLogViewModel -import org.terst.nav.track.SafState -import org.terst.nav.track.SavedTrackAdapter -import org.terst.nav.track.TrackDetailSheet -import org.terst.nav.ui.MainViewModel import java.io.File import java.io.FileOutputStream import java.util.Locale class VoiceLogFragment : Fragment() { - private val logViewModel by lazy { + private val viewModel by lazy { VoiceLogViewModel(repository = org.terst.nav.NavApplication.logbookRepository) } - private val mainViewModel: MainViewModel by activityViewModels() - private lateinit var layoutLogEntry: View private lateinit var speechRecognizer: SpeechRecognizer private lateinit var etNote: TextInputEditText private lateinit var fabMic: FloatingActionButton @@ -60,6 +48,7 @@ class VoiceLogFragment : Fragment() { private lateinit var btnSave: MaterialButton private lateinit var btnClear: MaterialButton private lateinit var tvSavedConfirmation: TextView + private lateinit var btnGenerateReport: MaterialButton private var pendingPhotoPath: String? = null @@ -92,7 +81,6 @@ class VoiceLogFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - layoutLogEntry = view.findViewById(R.id.layout_log_entry) etNote = view.findViewById(R.id.et_note) fabMic = view.findViewById(R.id.fab_mic) btnCamera = view.findViewById(R.id.btn_camera) @@ -104,6 +92,7 @@ class VoiceLogFragment : Fragment() { btnSave = view.findViewById(R.id.btn_save) btnClear = view.findViewById(R.id.btn_clear) tvSavedConfirmation = view.findViewById(R.id.tv_saved_confirmation) + btnGenerateReport = view.findViewById(R.id.btn_generate_report) setupSpeechRecognizer() @@ -112,76 +101,19 @@ class VoiceLogFragment : Fragment() { btnGallery.setOnClickListener { galleryLauncher.launch("image/*") } btnRemovePhoto.setOnClickListener { clearPhoto() } btnSave.setOnClickListener { - logViewModel.save(etNote.text?.toString()?.trim() ?: "", pendingPhotoPath) + viewModel.save(etNote.text?.toString()?.trim() ?: "", pendingPhotoPath) } btnClear.setOnClickListener { clearEntry() } - view.findViewById<MaterialButton>(R.id.btn_generate_report).setOnClickListener { + btnGenerateReport.setOnClickListener { parentFragmentManager.beginTransaction() .replace(R.id.fragment_container, org.terst.nav.tripreport.TripReportFragment()) .addToBackStack(null) .commit() } - // Show log-entry section only while recording viewLifecycleOwner.lifecycleScope.launch { - mainViewModel.isRecording.collect { recording -> - layoutLogEntry.visibility = if (recording) View.VISIBLE else View.GONE - } - } - - // Inline track list - val rv = view.findViewById<RecyclerView>(R.id.rv_saved_tracks) - val layoutEmpty = view.findViewById<View>(R.id.layout_empty) - val tvEmpty = view.findViewById<TextView>(R.id.tv_empty) - val btnSetup = view.findViewById<MaterialButton>(R.id.btn_setup_storage) - - rv.layoutManager = LinearLayoutManager(requireContext()) - rv.isNestedScrollingEnabled = false - rv.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) - - val tracksAdapter = SavedTrackAdapter { track -> - mainViewModel.selectTrack(track) - parentFragmentManager.beginTransaction() - .replace(R.id.fragment_container, TrackDetailSheet.newInstance()) - .addToBackStack("track_detail") - .commit() - } - rv.adapter = tracksAdapter - - viewLifecycleOwner.lifecycleScope.launch { - mainViewModel.savedTracks.collect { tracks -> - tracksAdapter.submitList(tracks) - layoutEmpty.visibility = if (tracks.isEmpty()) View.VISIBLE else View.GONE - rv.visibility = if (tracks.isEmpty()) View.GONE else View.VISIBLE - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - viewLifecycleOwner.lifecycleScope.launch { - mainViewModel.safState.collect { state -> - when (state) { - SafState.NOT_CONFIGURED -> { - tvEmpty.text = "No saved tracks yet.\nRecord a trip to see it here." - btnSetup.text = "Set up track storage" - btnSetup.visibility = View.VISIBLE - } - SafState.PERMISSION_LOST -> { - tvEmpty.text = "Track storage needs re-authorization." - btnSetup.text = "Restore track access" - btnSetup.visibility = View.VISIBLE - } - SafState.CONFIGURED -> btnSetup.visibility = View.GONE - } - } - } - btnSetup.setOnClickListener { - (requireActivity() as MainActivity).launchSafPicker() - } - } - - viewLifecycleOwner.lifecycleScope.launch { - logViewModel.state.collect { state -> renderState(state) } + viewModel.state.collect { state -> renderState(state) } } } @@ -195,14 +127,14 @@ class VoiceLogFragment : Fragment() { } speechRecognizer = SpeechRecognizer.createSpeechRecognizer(requireContext()) speechRecognizer.setRecognitionListener(object : RecognitionListener { - override fun onReadyForSpeech(params: Bundle?) { logViewModel.onListeningStarted() } + override fun onReadyForSpeech(params: Bundle?) { viewModel.onListeningStarted() } override fun onResults(results: Bundle?) { val text = results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) ?.firstOrNull() ?: "" - if (text.isNotBlank()) logViewModel.onSpeechRecognized(text) - else logViewModel.onRecognitionError("Could not understand speech") + if (text.isNotBlank()) viewModel.onSpeechRecognized(text) + else viewModel.onRecognitionError("Could not understand speech") } - override fun onError(error: Int) { logViewModel.onRecognitionError("Recognition error: $error") } + override fun onError(error: Int) { viewModel.onRecognitionError("Recognition error: $error") } override fun onBeginningOfSpeech() {} override fun onBufferReceived(b: ByteArray?) {} override fun onEndOfSpeech() {} @@ -245,7 +177,7 @@ class VoiceLogFragment : Fragment() { private fun clearEntry() { etNote.setText("") clearPhoto() - logViewModel.retry() + viewModel.retry() tvSavedConfirmation.text = "" tvStatus.text = "" } |
