From 201f13268625cf248b04dd3474c39666921a1168 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sat, 9 Jul 2022 10:17:23 -0600 Subject: [PATCH] detail: fix song deletion issue Fix an issue where the song dialog would not properly close when the song it corresponded to was deleted. --- .../oxycblt/auxio/detail/DetailViewModel.kt | 26 +++++++---- .../oxycblt/auxio/detail/SongDetailDialog.kt | 44 ++++++++++++------- .../auxio/music/system/IndexerService.kt | 2 + 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index ec034fb70..ce0097910 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -26,7 +26,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.oxycblt.auxio.R import org.oxycblt.auxio.detail.recycler.DiscHeader import org.oxycblt.auxio.detail.recycler.SortHeader @@ -54,8 +53,9 @@ import org.oxycblt.auxio.util.unlikelyToBeNull */ class DetailViewModel(application: Application) : AndroidViewModel(application), MusicStore.Callback { - data class DetailSong( - val song: Song, + data class DetailSong(val song: Song, val info: SongInfo?) + + data class SongInfo( val bitrateKbps: Int?, val sampleRate: Int?, val resolvedMimeType: MimeType @@ -156,12 +156,20 @@ class DetailViewModel(application: Application) : } private fun generateDetailSong(song: Song) { - viewModelScope.launch { - _currentSong.value = withContext(Dispatchers.IO) { generateDetailSongImpl(song) } + _currentSong.value = DetailSong(song, null) + viewModelScope.launch(Dispatchers.IO) { + val info = generateDetailSongInfo(song) + + // Theoretically, the song could have been changed again while we were + // extracting song information, so make sure that we can update the song + // in the first place. + if (_currentSong.value?.run { this.song.id } == song.id) { + _currentSong.value = DetailSong(song, info) + } } } - private fun generateDetailSongImpl(song: Song): DetailSong { + private fun generateDetailSongInfo(song: Song): SongInfo { val extractor = MediaExtractor() try { @@ -169,7 +177,7 @@ class DetailViewModel(application: Application) : } catch (e: Exception) { logW("Unable to extract song attributes.") logW(e.stackTraceToString()) - return DetailSong(song, null, null, song.mimeType) + return SongInfo(null, null, song.mimeType) } val format = extractor.getTrackFormat(0) @@ -203,7 +211,7 @@ class DetailViewModel(application: Application) : MimeType(song.mimeType.fromExtension, formatMimeType) } - return DetailSong(song, bitrate, sampleRate, resolvedMimeType) + return SongInfo(bitrate, sampleRate, resolvedMimeType) } private fun refreshAlbumData(album: Album) { @@ -258,6 +266,8 @@ class DetailViewModel(application: Application) : val newSong = library.sanitize(song.song) if (newSong != null) { generateDetailSong(newSong) + } else { + _currentSong.value = null } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt index 5609e5f49..efce445d8 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt @@ -22,6 +22,7 @@ import android.text.format.Formatter import android.view.LayoutInflater import androidx.appcompat.app.AlertDialog import androidx.core.view.isGone +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.DialogSongDetailBinding @@ -29,6 +30,7 @@ import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.logD class SongDetailDialog : ViewBindingDialogFragment() { private val detailModel: DetailViewModel by androidActivityViewModels() @@ -56,28 +58,38 @@ class SongDetailDialog : ViewBindingDialogFragment() { private fun updateSong(song: DetailViewModel.DetailSong?) { val binding = requireBinding() + logD("$song") + if (song != null) { - binding.detailContainer.isGone = false - binding.detailFileName.setText(song.song.path.name) - binding.detailRelativeDir.setText(song.song.path.parent.resolveName(requireContext())) - binding.detailFormat.setText(song.resolvedMimeType.resolveName(requireContext())) - binding.detailSize.setText(Formatter.formatFileSize(requireContext(), song.song.size)) - binding.detailDuration.setText(song.song.durationSecs.formatDuration(true)) + if (song.info != null) { + binding.detailContainer.isGone = false + binding.detailFileName.setText(song.song.path.name) + binding.detailRelativeDir.setText( + song.song.path.parent.resolveName(requireContext())) + binding.detailFormat.setText( + song.info.resolvedMimeType.resolveName(requireContext())) + binding.detailSize.setText( + Formatter.formatFileSize(requireContext(), song.song.size)) + binding.detailDuration.setText(song.song.durationSecs.formatDuration(true)) - if (song.bitrateKbps != null) { - binding.detailBitrate.setText(getString(R.string.fmt_bitrate, song.bitrateKbps)) - } else { - binding.detailBitrate.setText(R.string.def_bitrate) - } + if (song.info.bitrateKbps != null) { + binding.detailBitrate.setText( + getString(R.string.fmt_bitrate, song.info.bitrateKbps)) + } else { + binding.detailBitrate.setText(R.string.def_bitrate) + } - if (song.sampleRate != null) { - binding.detailSampleRate.setText( - getString(R.string.fmt_sample_rate, song.sampleRate)) + if (song.info.sampleRate != null) { + binding.detailSampleRate.setText( + getString(R.string.fmt_sample_rate, song.info.sampleRate)) + } else { + binding.detailSampleRate.setText(R.string.def_sample_rate) + } } else { - binding.detailSampleRate.setText(R.string.def_sample_rate) + binding.detailContainer.isGone = true } } else { - binding.detailContainer.isGone = true + findNavController().navigateUp() } } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt b/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt index 6dac988aa..3e0229c3d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt @@ -49,6 +49,8 @@ import org.oxycblt.auxio.util.logD * boilerplate you skip is not worth the insanity of androidx. * * @author OxygenCobalt + * + * TODO: Add abstractions for services. notifications, and generations */ class IndexerService : Service(), Indexer.Controller, Settings.Callback { private val indexer = Indexer.getInstance()