detail: fix song deletion issue

Fix an issue where the song dialog would not properly close when the
song it corresponded to was deleted.
This commit is contained in:
OxygenCobalt 2022-07-09 10:17:23 -06:00
parent 35f05ed902
commit 201f132686
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 48 additions and 24 deletions

View file

@ -26,7 +26,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.recycler.DiscHeader import org.oxycblt.auxio.detail.recycler.DiscHeader
import org.oxycblt.auxio.detail.recycler.SortHeader import org.oxycblt.auxio.detail.recycler.SortHeader
@ -54,8 +53,9 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
*/ */
class DetailViewModel(application: Application) : class DetailViewModel(application: Application) :
AndroidViewModel(application), MusicStore.Callback { AndroidViewModel(application), MusicStore.Callback {
data class DetailSong( data class DetailSong(val song: Song, val info: SongInfo?)
val song: Song,
data class SongInfo(
val bitrateKbps: Int?, val bitrateKbps: Int?,
val sampleRate: Int?, val sampleRate: Int?,
val resolvedMimeType: MimeType val resolvedMimeType: MimeType
@ -156,12 +156,20 @@ class DetailViewModel(application: Application) :
} }
private fun generateDetailSong(song: Song) { private fun generateDetailSong(song: Song) {
viewModelScope.launch { _currentSong.value = DetailSong(song, null)
_currentSong.value = withContext(Dispatchers.IO) { generateDetailSongImpl(song) } 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() val extractor = MediaExtractor()
try { try {
@ -169,7 +177,7 @@ class DetailViewModel(application: Application) :
} catch (e: Exception) { } catch (e: Exception) {
logW("Unable to extract song attributes.") logW("Unable to extract song attributes.")
logW(e.stackTraceToString()) logW(e.stackTraceToString())
return DetailSong(song, null, null, song.mimeType) return SongInfo(null, null, song.mimeType)
} }
val format = extractor.getTrackFormat(0) val format = extractor.getTrackFormat(0)
@ -203,7 +211,7 @@ class DetailViewModel(application: Application) :
MimeType(song.mimeType.fromExtension, formatMimeType) MimeType(song.mimeType.fromExtension, formatMimeType)
} }
return DetailSong(song, bitrate, sampleRate, resolvedMimeType) return SongInfo(bitrate, sampleRate, resolvedMimeType)
} }
private fun refreshAlbumData(album: Album) { private fun refreshAlbumData(album: Album) {
@ -258,6 +266,8 @@ class DetailViewModel(application: Application) :
val newSong = library.sanitize(song.song) val newSong = library.sanitize(song.song)
if (newSong != null) { if (newSong != null) {
generateDetailSong(newSong) generateDetailSong(newSong)
} else {
_currentSong.value = null
} }
} }

View file

@ -22,6 +22,7 @@ import android.text.format.Formatter
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogSongDetailBinding 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.androidActivityViewModels
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.formatDuration import org.oxycblt.auxio.util.formatDuration
import org.oxycblt.auxio.util.logD
class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() { class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
private val detailModel: DetailViewModel by androidActivityViewModels() private val detailModel: DetailViewModel by androidActivityViewModels()
@ -56,28 +58,38 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
private fun updateSong(song: DetailViewModel.DetailSong?) { private fun updateSong(song: DetailViewModel.DetailSong?) {
val binding = requireBinding() val binding = requireBinding()
logD("$song")
if (song != null) { if (song != null) {
binding.detailContainer.isGone = false if (song.info != null) {
binding.detailFileName.setText(song.song.path.name) binding.detailContainer.isGone = false
binding.detailRelativeDir.setText(song.song.path.parent.resolveName(requireContext())) binding.detailFileName.setText(song.song.path.name)
binding.detailFormat.setText(song.resolvedMimeType.resolveName(requireContext())) binding.detailRelativeDir.setText(
binding.detailSize.setText(Formatter.formatFileSize(requireContext(), song.song.size)) song.song.path.parent.resolveName(requireContext()))
binding.detailDuration.setText(song.song.durationSecs.formatDuration(true)) 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) { if (song.info.bitrateKbps != null) {
binding.detailBitrate.setText(getString(R.string.fmt_bitrate, song.bitrateKbps)) binding.detailBitrate.setText(
} else { getString(R.string.fmt_bitrate, song.info.bitrateKbps))
binding.detailBitrate.setText(R.string.def_bitrate) } else {
} binding.detailBitrate.setText(R.string.def_bitrate)
}
if (song.sampleRate != null) { if (song.info.sampleRate != null) {
binding.detailSampleRate.setText( binding.detailSampleRate.setText(
getString(R.string.fmt_sample_rate, song.sampleRate)) getString(R.string.fmt_sample_rate, song.info.sampleRate))
} else {
binding.detailSampleRate.setText(R.string.def_sample_rate)
}
} else { } else {
binding.detailSampleRate.setText(R.string.def_sample_rate) binding.detailContainer.isGone = true
} }
} else { } else {
binding.detailContainer.isGone = true findNavController().navigateUp()
} }
} }
} }

View file

@ -49,6 +49,8 @@ import org.oxycblt.auxio.util.logD
* boilerplate you skip is not worth the insanity of androidx. * boilerplate you skip is not worth the insanity of androidx.
* *
* @author OxygenCobalt * @author OxygenCobalt
*
* TODO: Add abstractions for services. notifications, and generations
*/ */
class IndexerService : Service(), Indexer.Controller, Settings.Callback { class IndexerService : Service(), Indexer.Controller, Settings.Callback {
private val indexer = Indexer.getInstance() private val indexer = Indexer.getInstance()