all: fix logging & anim unification

Can't bisect this without spending way too much time on it.
This commit is contained in:
Alexander Capehart 2024-10-18 16:09:32 -06:00
parent 7dfaea3a4b
commit 0f4702c4dd
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
118 changed files with 937 additions and 894 deletions

View file

@ -116,8 +116,7 @@ class AuxioService :
private fun getRootChildrenLimit(): Int { private fun getRootChildrenLimit(): Int {
return browserRootHints?.getInt( return browserRootHints?.getInt(
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, 4) MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, 4) ?: 4
?: 4
} }
private fun Bundle.getPage(): MusicServiceFragment.Page? { private fun Bundle.getPage(): MusicServiceFragment.Page? {

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.playback.state.DeferredPlayback
import org.oxycblt.auxio.ui.UISettings import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.util.isNight import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.systemBarInsetsCompat import org.oxycblt.auxio.util.systemBarInsetsCompat
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Auxio's single [AppCompatActivity]. * Auxio's single [AppCompatActivity].
@ -62,7 +62,7 @@ class MainActivity : AppCompatActivity() {
val binding = ActivityMainBinding.inflate(layoutInflater) val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setupEdgeToEdge(binding.root) setupEdgeToEdge(binding.root)
T.d("Activity created") L.d("Activity created")
} }
override fun onResume() { override fun onResume() {
@ -90,10 +90,10 @@ class MainActivity : AppCompatActivity() {
// Apply the color scheme. The black theme requires it's own set of themes since // Apply the color scheme. The black theme requires it's own set of themes since
// it's not possible to modify the themes at run-time. // it's not possible to modify the themes at run-time.
if (isNight && uiSettings.useBlackTheme) { if (isNight && uiSettings.useBlackTheme) {
T.d("Applying black theme [accent ${uiSettings.accent}]") L.d("Applying black theme [accent ${uiSettings.accent}]")
setTheme(uiSettings.accent.blackTheme) setTheme(uiSettings.accent.blackTheme)
} else { } else {
T.d("Applying normal theme [accent ${uiSettings.accent}]") L.d("Applying normal theme [accent ${uiSettings.accent}]")
setTheme(uiSettings.accent.theme) setTheme(uiSettings.accent.theme)
} }
} }
@ -120,7 +120,7 @@ class MainActivity : AppCompatActivity() {
private fun startIntentAction(intent: Intent?): Boolean { private fun startIntentAction(intent: Intent?): Boolean {
if (intent == null) { if (intent == null) {
// Nothing to do. // Nothing to do.
T.d("No intent to handle") L.d("No intent to handle")
return false return false
} }
@ -129,7 +129,7 @@ class MainActivity : AppCompatActivity() {
// This is because onStart can run multiple times, and thus we really don't // This is because onStart can run multiple times, and thus we really don't
// want to return false and override the original delayed action with a // want to return false and override the original delayed action with a
// RestoreState action. // RestoreState action.
T.d("Already used this intent") L.d("Already used this intent")
return true return true
} }
intent.putExtra(KEY_INTENT_USED, true) intent.putExtra(KEY_INTENT_USED, true)
@ -139,11 +139,11 @@ class MainActivity : AppCompatActivity() {
Intent.ACTION_VIEW -> DeferredPlayback.Open(intent.data ?: return false) Intent.ACTION_VIEW -> DeferredPlayback.Open(intent.data ?: return false)
Auxio.INTENT_KEY_SHORTCUT_SHUFFLE -> DeferredPlayback.ShuffleAll Auxio.INTENT_KEY_SHORTCUT_SHUFFLE -> DeferredPlayback.ShuffleAll
else -> { else -> {
T.w("Unexpected intent ${intent.action}") L.w("Unexpected intent ${intent.action}")
return false return false
} }
} }
T.d("Translated intent to $action") L.d("Translated intent to $action")
playbackModel.playDeferred(action) playbackModel.playDeferred(action)
return true return true
} }

View file

@ -68,7 +68,7 @@ import org.oxycblt.auxio.util.getDimen
import org.oxycblt.auxio.util.lazyReflectedMethod import org.oxycblt.auxio.util.lazyReflectedMethod
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A wrapper around the home fragment that shows the playback fragment and high-level navigation. * A wrapper around the home fragment that shows the playback fragment and high-level navigation.
@ -145,13 +145,13 @@ class MainFragment :
if (queueSheetBehavior != null) { if (queueSheetBehavior != null) {
// In portrait mode, set up click listeners on the stacked sheets. // In portrait mode, set up click listeners on the stacked sheets.
T.d("Configuring stacked bottom sheets") L.d("Configuring stacked bottom sheets")
unlikelyToBeNull(binding.queueHandleWrapper).setOnClickListener { unlikelyToBeNull(binding.queueHandleWrapper).setOnClickListener {
playbackModel.openQueue() playbackModel.openQueue()
} }
} else { } else {
// Dual-pane mode, manually style the static queue sheet. // Dual-pane mode, manually style the static queue sheet.
T.d("Configuring dual-pane bottom sheet") L.d("Configuring dual-pane bottom sheet")
binding.queueSheet.apply { binding.queueSheet.apply {
// Emulate the elevated bottom sheet style. // Emulate the elevated bottom sheet style.
background = background =
@ -367,11 +367,11 @@ class MainFragment :
override fun onActionSelected(actionItem: SpeedDialActionItem): Boolean { override fun onActionSelected(actionItem: SpeedDialActionItem): Boolean {
when (actionItem.id) { when (actionItem.id) {
R.id.action_new_playlist -> { R.id.action_new_playlist -> {
T.d("Creating playlist") L.d("Creating playlist")
musicModel.createPlaylist() musicModel.createPlaylist()
} }
R.id.action_import_playlist -> { R.id.action_import_playlist -> {
T.d("Importing playlist") L.d("Importing playlist")
musicModel.importPlaylist() musicModel.importPlaylist()
} }
else -> {} else -> {}
@ -402,7 +402,7 @@ class MainFragment :
// 1. Loading placeholder for item lists // 1. Loading placeholder for item lists
// 2. Rework the "No Music" case to not be an error and instead result in a placeholder // 2. Rework the "No Music" case to not be an error and instead result in a placeholder
if (state is IndexingState.Completed && state.error == null) { if (state is IndexingState.Completed && state.error == null) {
T.d("Received ok response") L.d("Received ok response")
val binding = requireBinding() val binding = requireBinding()
updateFabVisibility( updateFabVisibility(
binding, binding,
@ -427,7 +427,7 @@ class MainFragment :
// displaying the shuffle FAB makes no sense. We also don't want the fast scroll // displaying the shuffle FAB makes no sense. We also don't want the fast scroll
// popup to overlap with the FAB, so we hide the FAB when fast scrolling too. // popup to overlap with the FAB, so we hide the FAB when fast scrolling too.
if (shouldHideAllFabs(binding, songs, isFastScrolling)) { if (shouldHideAllFabs(binding, songs, isFastScrolling)) {
T.d("Hiding fab: [empty: ${songs.isEmpty()} scrolling: $isFastScrolling]") L.d("Hiding fab: [empty: ${songs.isEmpty()} scrolling: $isFastScrolling]")
forceHideAllFabs() forceHideAllFabs()
} else { } else {
if (tabType != MusicType.PLAYLISTS) { if (tabType != MusicType.PLAYLISTS) {
@ -436,7 +436,7 @@ class MainFragment :
} }
if (binding.homeNewPlaylistFab.mainFab.isOrWillBeShown) { if (binding.homeNewPlaylistFab.mainFab.isOrWillBeShown) {
T.d("Animating transition") L.d("Animating transition")
binding.homeNewPlaylistFab.hide( binding.homeNewPlaylistFab.hide(
object : FloatingActionButton.OnVisibilityChangedListener() { object : FloatingActionButton.OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton) { override fun onHidden(fab: FloatingActionButton) {
@ -451,17 +451,17 @@ class MainFragment :
} }
}) })
} else { } else {
T.d("Showing immediately") L.d("Showing immediately")
binding.homeShuffleFab.show() binding.homeShuffleFab.show()
} }
} else { } else {
T.d("Showing playlist button") L.d("Showing playlist button")
if (binding.homeNewPlaylistFab.mainFab.isOrWillBeShown) { if (binding.homeNewPlaylistFab.mainFab.isOrWillBeShown) {
return return
} }
if (binding.homeShuffleFab.isOrWillBeShown) { if (binding.homeShuffleFab.isOrWillBeShown) {
T.d("Animating transition") L.d("Animating transition")
binding.homeShuffleFab.hide( binding.homeShuffleFab.hide(
object : FloatingActionButton.OnVisibilityChangedListener() { object : FloatingActionButton.OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton) { override fun onHidden(fab: FloatingActionButton) {
@ -476,7 +476,7 @@ class MainFragment :
} }
}) })
} else { } else {
T.d("Showing immediately") L.d("Showing immediately")
binding.homeNewPlaylistFab.show() binding.homeNewPlaylistFab.show()
} }
} }
@ -551,7 +551,7 @@ class MainFragment :
private fun handlePanel(panel: OpenPanel?) { private fun handlePanel(panel: OpenPanel?) {
if (panel == null) return if (panel == null) return
T.d("Trying to update panel to $panel") L.d("Trying to update panel to $panel")
when (panel) { when (panel) {
OpenPanel.MAIN -> tryClosePlaybackPanel() OpenPanel.MAIN -> tryClosePlaybackPanel()
OpenPanel.PLAYBACK -> tryOpenPlaybackPanel() OpenPanel.PLAYBACK -> tryOpenPlaybackPanel()
@ -567,7 +567,7 @@ class MainFragment :
if (playbackSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_COLLAPSED) { if (playbackSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_COLLAPSED) {
// Playback sheet is not expanded and not hidden, we can expand it. // Playback sheet is not expanded and not hidden, we can expand it.
T.d("Expanding playback sheet") L.d("Expanding playback sheet")
playbackSheetBehavior.state = BackportBottomSheetBehavior.STATE_EXPANDED playbackSheetBehavior.state = BackportBottomSheetBehavior.STATE_EXPANDED
return return
} }
@ -578,7 +578,7 @@ class MainFragment :
queueSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_EXPANDED) { queueSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_EXPANDED) {
// Queue sheet and playback sheet is expanded, close the queue sheet so the // Queue sheet and playback sheet is expanded, close the queue sheet so the
// playback panel can shown. // playback panel can shown.
T.d("Collapsing queue sheet") L.d("Collapsing queue sheet")
queueSheetBehavior.state = BackportBottomSheetBehavior.STATE_COLLAPSED queueSheetBehavior.state = BackportBottomSheetBehavior.STATE_COLLAPSED
} }
} }
@ -589,7 +589,7 @@ class MainFragment :
binding.playbackSheet.coordinatorLayoutBehavior as PlaybackBottomSheetBehavior binding.playbackSheet.coordinatorLayoutBehavior as PlaybackBottomSheetBehavior
if (playbackSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_EXPANDED) { if (playbackSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_EXPANDED) {
// Playback sheet (and possibly queue) needs to be collapsed. // Playback sheet (and possibly queue) needs to be collapsed.
T.d("Collapsing playback and queue sheets") L.d("Collapsing playback and queue sheets")
val queueSheetBehavior = val queueSheetBehavior =
binding.queueSheet.coordinatorLayoutBehavior as QueueBottomSheetBehavior? binding.queueSheet.coordinatorLayoutBehavior as QueueBottomSheetBehavior?
playbackSheetBehavior.state = BackportBottomSheetBehavior.STATE_COLLAPSED playbackSheetBehavior.state = BackportBottomSheetBehavior.STATE_COLLAPSED
@ -615,7 +615,7 @@ class MainFragment :
val playbackSheetBehavior = val playbackSheetBehavior =
binding.playbackSheet.coordinatorLayoutBehavior as PlaybackBottomSheetBehavior binding.playbackSheet.coordinatorLayoutBehavior as PlaybackBottomSheetBehavior
if (playbackSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_HIDDEN) { if (playbackSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_HIDDEN) {
T.d("Unhiding and enabling playback sheet") L.d("Unhiding and enabling playback sheet")
val queueSheetBehavior = val queueSheetBehavior =
binding.queueSheet.coordinatorLayoutBehavior as QueueBottomSheetBehavior? binding.queueSheet.coordinatorLayoutBehavior as QueueBottomSheetBehavior?
// Queue sheet behavior is either collapsed or expanded, no hiding needed // Queue sheet behavior is either collapsed or expanded, no hiding needed
@ -636,7 +636,7 @@ class MainFragment :
val queueSheetBehavior = val queueSheetBehavior =
binding.queueSheet.coordinatorLayoutBehavior as QueueBottomSheetBehavior? binding.queueSheet.coordinatorLayoutBehavior as QueueBottomSheetBehavior?
T.d("Hiding and disabling playback and queue sheets") L.d("Hiding and disabling playback and queue sheets")
// Make both bottom sheets non-draggable so the user can't halt the hiding event. // Make both bottom sheets non-draggable so the user can't halt the hiding event.
queueSheetBehavior?.apply { queueSheetBehavior?.apply {
@ -720,7 +720,7 @@ class MainFragment :
OnBackPressedCallback(false) { OnBackPressedCallback(false) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
if (detailModel.dropPlaylistEdit()) { if (detailModel.dropPlaylistEdit()) {
T.d("Dropped playlist edits") L.d("Dropped playlist edits")
} }
} }
@ -733,7 +733,7 @@ class MainFragment :
OnBackPressedCallback(false) { OnBackPressedCallback(false) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
if (listModel.dropSelection()) { if (listModel.dropSelection()) {
T.d("Dropped selection") L.d("Dropped selection")
} }
} }

View file

@ -44,7 +44,7 @@ import org.oxycblt.auxio.util.getPlural
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ListFragment] that shows information about an [Album]. * A [ListFragment] that shows information about an [Album].
@ -103,7 +103,7 @@ class AlbumDetailFragment : DetailFragment<Album, Song>() {
private fun updateAlbum(album: Album?) { private fun updateAlbum(album: Album?) {
if (album == null) { if (album == null) {
T.d("No album to show, navigating away") L.d("No album to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }
@ -153,7 +153,7 @@ class AlbumDetailFragment : DetailFragment<Album, Song>() {
val binding = requireBinding() val binding = requireBinding()
when (show) { when (show) {
is Show.SongDetails -> { is Show.SongDetails -> {
T.d("Navigating to ${show.song}") L.d("Navigating to ${show.song}")
findNavController() findNavController()
.navigateSafe(AlbumDetailFragmentDirections.showSong(show.song.uid)) .navigateSafe(AlbumDetailFragmentDirections.showSong(show.song.uid))
} }
@ -162,11 +162,11 @@ class AlbumDetailFragment : DetailFragment<Album, Song>() {
// fragment should be launched otherwise. // fragment should be launched otherwise.
is Show.SongAlbumDetails -> { is Show.SongAlbumDetails -> {
if (unlikelyToBeNull(detailModel.currentAlbum.value) == show.song.album) { if (unlikelyToBeNull(detailModel.currentAlbum.value) == show.song.album) {
T.d("Navigating to a ${show.song} in this album") L.d("Navigating to a ${show.song} in this album")
scrollToAlbumSong(show.song) scrollToAlbumSong(show.song)
detailModel.toShow.consume() detailModel.toShow.consume()
} else { } else {
T.d("Navigating to the album of ${show.song}") L.d("Navigating to the album of ${show.song}")
findNavController() findNavController()
.navigateSafe(AlbumDetailFragmentDirections.showAlbum(show.song.album.uid)) .navigateSafe(AlbumDetailFragmentDirections.showAlbum(show.song.album.uid))
} }
@ -176,27 +176,27 @@ class AlbumDetailFragment : DetailFragment<Album, Song>() {
// detail fragment. // detail fragment.
is Show.AlbumDetails -> { is Show.AlbumDetails -> {
if (unlikelyToBeNull(detailModel.currentAlbum.value) == show.album) { if (unlikelyToBeNull(detailModel.currentAlbum.value) == show.album) {
T.d("Navigating to the top of this album") L.d("Navigating to the top of this album")
binding.detailRecycler.scrollToPosition(0) binding.detailRecycler.scrollToPosition(0)
detailModel.toShow.consume() detailModel.toShow.consume()
} else { } else {
T.d("Navigating to ${show.album}") L.d("Navigating to ${show.album}")
findNavController() findNavController()
.navigateSafe(AlbumDetailFragmentDirections.showAlbum(show.album.uid)) .navigateSafe(AlbumDetailFragmentDirections.showAlbum(show.album.uid))
} }
} }
is Show.ArtistDetails -> { is Show.ArtistDetails -> {
T.d("Navigating to ${show.artist}") L.d("Navigating to ${show.artist}")
findNavController() findNavController()
.navigateSafe(AlbumDetailFragmentDirections.showArtist(show.artist.uid)) .navigateSafe(AlbumDetailFragmentDirections.showArtist(show.artist.uid))
} }
is Show.SongArtistDecision -> { is Show.SongArtistDecision -> {
T.d("Navigating to artist choices for ${show.song}") L.d("Navigating to artist choices for ${show.song}")
findNavController() findNavController()
.navigateSafe(AlbumDetailFragmentDirections.showArtistChoices(show.song.uid)) .navigateSafe(AlbumDetailFragmentDirections.showArtistChoices(show.song.uid))
} }
is Show.AlbumArtistDecision -> { is Show.AlbumArtistDecision -> {
T.d("Navigating to artist choices for ${show.album}") L.d("Navigating to artist choices for ${show.album}")
findNavController() findNavController()
.navigateSafe(AlbumDetailFragmentDirections.showArtistChoices(show.album.uid)) .navigateSafe(AlbumDetailFragmentDirections.showArtistChoices(show.album.uid))
} }
@ -239,7 +239,7 @@ class AlbumDetailFragment : DetailFragment<Album, Song>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
T.d("Adding ${decision.songs.size} songs to a playlist") L.d("Adding ${decision.songs.size} songs to a playlist")
AlbumDetailFragmentDirections.addToPlaylist( AlbumDetailFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
@ -268,11 +268,11 @@ class AlbumDetailFragment : DetailFragment<Album, Song>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaybackDecision.PlayFromArtist -> { is PlaybackDecision.PlayFromArtist -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
AlbumDetailFragmentDirections.playFromArtist(decision.song.uid) AlbumDetailFragmentDirections.playFromArtist(decision.song.uid)
} }
is PlaybackDecision.PlayFromGenre -> { is PlaybackDecision.PlayFromGenre -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
AlbumDetailFragmentDirections.playFromGenre(decision.song.uid) AlbumDetailFragmentDirections.playFromGenre(decision.song.uid)
} }
} }

View file

@ -44,7 +44,7 @@ import org.oxycblt.auxio.util.getPlural
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ListFragment] that shows information about an [Artist]. * A [ListFragment] that shows information about an [Artist].
@ -111,7 +111,7 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
private fun updateArtist(artist: Artist?) { private fun updateArtist(artist: Artist?) {
if (artist == null) { if (artist == null) {
T.d("No artist to show, navigating away") L.d("No artist to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }
@ -154,7 +154,7 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
// The artist does not have any songs, so hide functionality that makes no sense. // The artist does not have any songs, so hide functionality that makes no sense.
// ex. Play and Shuffle, Song Counts, and Genre Information. // ex. Play and Shuffle, Song Counts, and Genre Information.
// Artists are always guaranteed to have albums however, so continue to show those. // Artists are always guaranteed to have albums however, so continue to show those.
T.d("Artist is empty, disabling genres and playback") L.d("Artist is empty, disabling genres and playback")
binding.detailSubhead.isVisible = false binding.detailSubhead.isVisible = false
binding.detailPlayButton?.isEnabled = false binding.detailPlayButton?.isEnabled = false
binding.detailShuffleButton?.isEnabled = false binding.detailShuffleButton?.isEnabled = false
@ -176,14 +176,14 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
val binding = requireBinding() val binding = requireBinding()
when (show) { when (show) {
is Show.SongDetails -> { is Show.SongDetails -> {
T.d("Navigating to ${show.song}") L.d("Navigating to ${show.song}")
findNavController() findNavController()
.navigateSafe(ArtistDetailFragmentDirections.showSong(show.song.uid)) .navigateSafe(ArtistDetailFragmentDirections.showSong(show.song.uid))
} }
// Songs should be shown in their album, not in their artist. // Songs should be shown in their album, not in their artist.
is Show.SongAlbumDetails -> { is Show.SongAlbumDetails -> {
T.d("Navigating to the album of ${show.song}") L.d("Navigating to the album of ${show.song}")
findNavController() findNavController()
.navigateSafe(ArtistDetailFragmentDirections.showAlbum(show.song.album.uid)) .navigateSafe(ArtistDetailFragmentDirections.showAlbum(show.song.album.uid))
} }
@ -191,7 +191,7 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
// Launch a new detail view for an album, even if it is part of // Launch a new detail view for an album, even if it is part of
// this artist. // this artist.
is Show.AlbumDetails -> { is Show.AlbumDetails -> {
T.d("Navigating to ${show.album}") L.d("Navigating to ${show.album}")
findNavController() findNavController()
.navigateSafe(ArtistDetailFragmentDirections.showAlbum(show.album.uid)) .navigateSafe(ArtistDetailFragmentDirections.showAlbum(show.album.uid))
} }
@ -200,22 +200,22 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
// scroll back to the top. Otherwise launch a new detail view. // scroll back to the top. Otherwise launch a new detail view.
is Show.ArtistDetails -> { is Show.ArtistDetails -> {
if (show.artist == detailModel.currentArtist.value) { if (show.artist == detailModel.currentArtist.value) {
T.d("Navigating to the top of this artist") L.d("Navigating to the top of this artist")
binding.detailRecycler.scrollToPosition(0) binding.detailRecycler.scrollToPosition(0)
detailModel.toShow.consume() detailModel.toShow.consume()
} else { } else {
T.d("Navigating to ${show.artist}") L.d("Navigating to ${show.artist}")
findNavController() findNavController()
.navigateSafe(ArtistDetailFragmentDirections.showArtist(show.artist.uid)) .navigateSafe(ArtistDetailFragmentDirections.showArtist(show.artist.uid))
} }
} }
is Show.SongArtistDecision -> { is Show.SongArtistDecision -> {
T.d("Navigating to artist choices for ${show.song}") L.d("Navigating to artist choices for ${show.song}")
findNavController() findNavController()
.navigateSafe(ArtistDetailFragmentDirections.showArtistChoices(show.song.uid)) .navigateSafe(ArtistDetailFragmentDirections.showArtistChoices(show.song.uid))
} }
is Show.AlbumArtistDecision -> { is Show.AlbumArtistDecision -> {
T.d("Navigating to artist choices for ${show.album}") L.d("Navigating to artist choices for ${show.album}")
findNavController() findNavController()
.navigateSafe(ArtistDetailFragmentDirections.showArtistChoices(show.album.uid)) .navigateSafe(ArtistDetailFragmentDirections.showArtistChoices(show.album.uid))
} }
@ -259,7 +259,7 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
T.d("Adding ${decision.songs.size} songs to a playlist") L.d("Adding ${decision.songs.size} songs to a playlist")
ArtistDetailFragmentDirections.addToPlaylist( ArtistDetailFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
@ -300,7 +300,7 @@ class ArtistDetailFragment : DetailFragment<Artist, Music>() {
is PlaybackDecision.PlayFromArtist -> is PlaybackDecision.PlayFromArtist ->
error("Unexpected playback decision $decision") error("Unexpected playback decision $decision")
is PlaybackDecision.PlayFromGenre -> { is PlaybackDecision.PlayFromGenre -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
ArtistDetailFragmentDirections.playFromGenre(decision.song.uid) ArtistDetailFragmentDirections.playFromGenre(decision.song.uid)
} }
} }

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.info.Disc import org.oxycblt.auxio.music.info.Disc
import org.oxycblt.auxio.music.info.ReleaseType import org.oxycblt.auxio.music.info.ReleaseType
import timber.log.Timber as T import timber.log.Timber as L
interface DetailGenerator { interface DetailGenerator {
fun any(uid: Music.UID): Detail<out MusicParent>? fun any(uid: Music.UID): Detail<out MusicParent>?
@ -159,7 +159,7 @@ private class DetailGeneratorImpl(
// groupByTo normally returns a mapping to a MutableList mapping. Since MutableList // groupByTo normally returns a mapping to a MutableList mapping. Since MutableList
// inherits list, we can cast upwards and save a copy by directly inserting the // inherits list, we can cast upwards and save a copy by directly inserting the
// implicit album list into the mapping. // implicit album list into the mapping.
T.d("Implicit albums present, adding to list") L.d("Implicit albums present, adding to list")
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
(grouping as MutableMap<DetailSection.Albums.Category, Collection<Album>>)[ (grouping as MutableMap<DetailSection.Albums.Category, Collection<Album>>)[
DetailSection.Albums.Category.APPEARANCES] = artist.implicitAlbums DetailSection.Albums.Category.APPEARANCES] = artist.implicitAlbums

View file

@ -55,7 +55,7 @@ import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.util.Event import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent import org.oxycblt.auxio.util.MutableEvent
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* [ViewModel] that manages the Song, Album, Artist, and Genre detail views. Keeps track of the * [ViewModel] that manages the Song, Album, Artist, and Genre detail views. Keeps track of the
@ -301,7 +301,7 @@ constructor(
private fun showImpl(show: Show) { private fun showImpl(show: Show) {
val existing = toShow.flow.value val existing = toShow.flow.value
if (existing != null) { if (existing != null) {
T.d("Already have pending show command $existing, ignoring $show") L.d("Already have pending show command $existing, ignoring $show")
return return
} }
_toShow.put(show) _toShow.put(show)
@ -314,10 +314,10 @@ constructor(
* @param uid The UID of the [Song] to load. Must be valid. * @param uid The UID of the [Song] to load. Must be valid.
*/ */
fun setSong(uid: Music.UID) { fun setSong(uid: Music.UID) {
T.d("Opening song $uid") L.d("Opening song $uid")
_currentSong.value = musicRepository.deviceLibrary?.findSong(uid)?.also(::refreshAudioInfo) _currentSong.value = musicRepository.deviceLibrary?.findSong(uid)?.also(::refreshAudioInfo)
if (_currentSong.value == null) { if (_currentSong.value == null) {
T.w("Given song UID was invalid") L.w("Given song UID was invalid")
} }
} }
@ -328,14 +328,14 @@ constructor(
* @param uid The [Music.UID] of the [Album] to update [currentAlbum] to. Must be valid. * @param uid The [Music.UID] of the [Album] to update [currentAlbum] to. Must be valid.
*/ */
fun setAlbum(uid: Music.UID) { fun setAlbum(uid: Music.UID) {
T.d("Opening album $uid") L.d("Opening album $uid")
if (uid === _currentAlbum.value?.uid) { if (uid === _currentAlbum.value?.uid) {
return return
} }
val album = detailGenerator.album(uid) val album = detailGenerator.album(uid)
refreshDetail(album, _currentAlbum, _albumSongList, _albumSongInstructions, null) refreshDetail(album, _currentAlbum, _albumSongList, _albumSongInstructions, null)
if (_currentAlbum.value == null) { if (_currentAlbum.value == null) {
T.w("Given album UID was invalid") L.w("Given album UID was invalid")
} }
} }
@ -355,7 +355,7 @@ constructor(
* @param uid The [Music.UID] of the [Artist] to update [currentArtist] to. Must be valid. * @param uid The [Music.UID] of the [Artist] to update [currentArtist] to. Must be valid.
*/ */
fun setArtist(uid: Music.UID) { fun setArtist(uid: Music.UID) {
T.d("Opening artist $uid") L.d("Opening artist $uid")
if (uid === _currentArtist.value?.uid) { if (uid === _currentArtist.value?.uid) {
return return
} }
@ -379,7 +379,7 @@ constructor(
* @param uid The [Music.UID] of the [Genre] to update [currentGenre] to. Must be valid. * @param uid The [Music.UID] of the [Genre] to update [currentGenre] to. Must be valid.
*/ */
fun setGenre(uid: Music.UID) { fun setGenre(uid: Music.UID) {
T.d("Opening genre $uid") L.d("Opening genre $uid")
if (uid === _currentGenre.value?.uid) { if (uid === _currentGenre.value?.uid) {
return return
} }
@ -403,7 +403,7 @@ constructor(
* @param uid The [Music.UID] of the [Playlist] to update [currentPlaylist] to. Must be valid. * @param uid The [Music.UID] of the [Playlist] to update [currentPlaylist] to. Must be valid.
*/ */
fun setPlaylist(uid: Music.UID) { fun setPlaylist(uid: Music.UID) {
T.d("Opening playlist $uid") L.d("Opening playlist $uid")
if (uid === _currentPlaylist.value?.uid) { if (uid === _currentPlaylist.value?.uid) {
return return
} }
@ -413,7 +413,7 @@ constructor(
/** Start a playlist editing session. Does nothing if a playlist is not being shown. */ /** Start a playlist editing session. Does nothing if a playlist is not being shown. */
fun startPlaylistEdit() { fun startPlaylistEdit() {
val playlist = _currentPlaylist.value ?: return val playlist = _currentPlaylist.value ?: return
T.d("Starting playlist edit") L.d("Starting playlist edit")
_editedPlaylist.value = playlist.songs _editedPlaylist.value = playlist.songs
refreshPlaylist(playlist.uid) refreshPlaylist(playlist.uid)
} }
@ -425,7 +425,7 @@ constructor(
fun savePlaylistEdit() { fun savePlaylistEdit() {
val playlist = _currentPlaylist.value ?: return val playlist = _currentPlaylist.value ?: return
val editedPlaylist = _editedPlaylist.value ?: return val editedPlaylist = _editedPlaylist.value ?: return
T.d("Committing playlist edits") L.d("Committing playlist edits")
viewModelScope.launch { viewModelScope.launch {
musicRepository.rewritePlaylist(playlist, editedPlaylist) musicRepository.rewritePlaylist(playlist, editedPlaylist)
// TODO: The user could probably press some kind of button if they were fast enough. // TODO: The user could probably press some kind of button if they were fast enough.
@ -478,7 +478,7 @@ constructor(
if (realFrom !in editedPlaylist.indices || realTo !in editedPlaylist.indices) { if (realFrom !in editedPlaylist.indices || realTo !in editedPlaylist.indices) {
return false return false
} }
T.d("Moving playlist song from $realFrom [$from] to $realTo [$to]") L.d("Moving playlist song from $realFrom [$from] to $realTo [$to]")
editedPlaylist.add(realFrom, editedPlaylist.removeAt(realTo)) editedPlaylist.add(realFrom, editedPlaylist.removeAt(realTo))
_editedPlaylist.value = editedPlaylist _editedPlaylist.value = editedPlaylist
refreshPlaylist(playlist.uid, UpdateInstructions.Move(from, to)) refreshPlaylist(playlist.uid, UpdateInstructions.Move(from, to))
@ -497,7 +497,7 @@ constructor(
if (realAt !in editedPlaylist.indices) { if (realAt !in editedPlaylist.indices) {
return return
} }
T.d("Removing playlist song at $realAt [$at]") L.d("Removing playlist song at $realAt [$at]")
editedPlaylist.removeAt(realAt) editedPlaylist.removeAt(realAt)
_editedPlaylist.value = editedPlaylist _editedPlaylist.value = editedPlaylist
refreshPlaylist( refreshPlaylist(
@ -505,13 +505,13 @@ constructor(
if (editedPlaylist.isNotEmpty()) { if (editedPlaylist.isNotEmpty()) {
UpdateInstructions.Remove(at, 1) UpdateInstructions.Remove(at, 1)
} else { } else {
T.d("Playlist will be empty after removal, removing header") L.d("Playlist will be empty after removal, removing header")
UpdateInstructions.Remove(at - 1, 3) UpdateInstructions.Remove(at - 1, 3)
}) })
} }
private fun refreshAudioInfo(song: Song) { private fun refreshAudioInfo(song: Song) {
T.d("Refreshing audio info") L.d("Refreshing audio info")
// Clear any previous job in order to avoid stale data from appearing in the UI. // Clear any previous job in order to avoid stale data from appearing in the UI.
currentSongJob?.cancel() currentSongJob?.cancel()
_songAudioProperties.value = null _songAudioProperties.value = null
@ -519,7 +519,7 @@ constructor(
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val info = audioPropertiesFactory.extract(song) val info = audioPropertiesFactory.extract(song)
yield() yield()
T.d("Updating audio info to $info") L.d("Updating audio info to $info")
_songAudioProperties.value = info _songAudioProperties.value = info
} }
} }
@ -586,7 +586,7 @@ constructor(
uid: Music.UID, uid: Music.UID,
instructions: UpdateInstructions = UpdateInstructions.Diff instructions: UpdateInstructions = UpdateInstructions.Diff
) { ) {
T.d("Refreshing playlist list") L.d("Refreshing playlist list")
val edited = editedPlaylist.value val edited = editedPlaylist.value
if (edited == null) { if (edited == null) {
val playlist = detailGenerator.playlist(uid) val playlist = detailGenerator.playlist(uid)

View file

@ -43,7 +43,7 @@ import org.oxycblt.auxio.util.getPlural
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ListFragment] that shows information for a particular [Genre]. * A [ListFragment] that shows information for a particular [Genre].
@ -110,7 +110,7 @@ class GenreDetailFragment : DetailFragment<Genre, Music>() {
private fun updateGenre(genre: Genre?) { private fun updateGenre(genre: Genre?) {
if (genre == null) { if (genre == null) {
T.d("No genre to show, navigating away") L.d("No genre to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }
@ -144,7 +144,7 @@ class GenreDetailFragment : DetailFragment<Genre, Music>() {
private fun handleShow(show: Show?) { private fun handleShow(show: Show?) {
when (show) { when (show) {
is Show.SongDetails -> { is Show.SongDetails -> {
T.d("Navigating to ${show.song}") L.d("Navigating to ${show.song}")
findNavController() findNavController()
.navigateSafe(GenreDetailFragmentDirections.showSong(show.song.uid)) .navigateSafe(GenreDetailFragmentDirections.showSong(show.song.uid))
} }
@ -152,7 +152,7 @@ class GenreDetailFragment : DetailFragment<Genre, Music>() {
// Songs should be scrolled to if the album matches, or a new detail // Songs should be scrolled to if the album matches, or a new detail
// fragment should be launched otherwise. // fragment should be launched otherwise.
is Show.SongAlbumDetails -> { is Show.SongAlbumDetails -> {
T.d("Navigating to the album of ${show.song}") L.d("Navigating to the album of ${show.song}")
findNavController() findNavController()
.navigateSafe(GenreDetailFragmentDirections.showAlbum(show.song.album.uid)) .navigateSafe(GenreDetailFragmentDirections.showAlbum(show.song.album.uid))
} }
@ -160,29 +160,29 @@ class GenreDetailFragment : DetailFragment<Genre, Music>() {
// If the album matches, no need to do anything. Otherwise launch a new // If the album matches, no need to do anything. Otherwise launch a new
// detail fragment. // detail fragment.
is Show.AlbumDetails -> { is Show.AlbumDetails -> {
T.d("Navigating to ${show.album}") L.d("Navigating to ${show.album}")
findNavController() findNavController()
.navigateSafe(GenreDetailFragmentDirections.showAlbum(show.album.uid)) .navigateSafe(GenreDetailFragmentDirections.showAlbum(show.album.uid))
} }
// Always launch a new ArtistDetailFragment. // Always launch a new ArtistDetailFragment.
is Show.ArtistDetails -> { is Show.ArtistDetails -> {
T.d("Navigating to ${show.artist}") L.d("Navigating to ${show.artist}")
findNavController() findNavController()
.navigateSafe(GenreDetailFragmentDirections.showArtist(show.artist.uid)) .navigateSafe(GenreDetailFragmentDirections.showArtist(show.artist.uid))
} }
is Show.SongArtistDecision -> { is Show.SongArtistDecision -> {
T.d("Navigating to artist choices for ${show.song}") L.d("Navigating to artist choices for ${show.song}")
findNavController() findNavController()
.navigateSafe(GenreDetailFragmentDirections.showArtistChoices(show.song.uid)) .navigateSafe(GenreDetailFragmentDirections.showArtistChoices(show.song.uid))
} }
is Show.AlbumArtistDecision -> { is Show.AlbumArtistDecision -> {
T.d("Navigating to artist choices for ${show.album}") L.d("Navigating to artist choices for ${show.album}")
findNavController() findNavController()
.navigateSafe(GenreDetailFragmentDirections.showArtistChoices(show.album.uid)) .navigateSafe(GenreDetailFragmentDirections.showArtistChoices(show.album.uid))
} }
is Show.GenreDetails -> { is Show.GenreDetails -> {
T.d("Navigated to this genre") L.d("Navigated to this genre")
detailModel.toShow.consume() detailModel.toShow.consume()
} }
is Show.PlaylistDetails -> { is Show.PlaylistDetails -> {
@ -223,7 +223,7 @@ class GenreDetailFragment : DetailFragment<Genre, Music>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
T.d("Adding ${decision.songs.size} songs to a playlist") L.d("Adding ${decision.songs.size} songs to a playlist")
GenreDetailFragmentDirections.addToPlaylist( GenreDetailFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
@ -262,7 +262,7 @@ class GenreDetailFragment : DetailFragment<Genre, Music>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaybackDecision.PlayFromArtist -> { is PlaybackDecision.PlayFromArtist -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
GenreDetailFragmentDirections.playFromArtist(decision.song.uid) GenreDetailFragmentDirections.playFromArtist(decision.song.uid)
} }
is PlaybackDecision.PlayFromGenre -> error("Unexpected playback decision $decision") is PlaybackDecision.PlayFromGenre -> error("Unexpected playback decision $decision")

View file

@ -52,7 +52,7 @@ import org.oxycblt.auxio.util.getPlural
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ListFragment] that shows information for a particular [Playlist]. * A [ListFragment] that shows information for a particular [Playlist].
@ -81,11 +81,11 @@ class PlaylistDetailFragment :
getContentLauncher = getContentLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri == null) { if (uri == null) {
T.w("No URI returned from file picker") L.w("No URI returned from file picker")
return@registerForActivityResult return@registerForActivityResult
} }
T.d("Received playlist URI $uri") L.d("Received playlist URI $uri")
musicModel.importPlaylist(uri, pendingImportTarget) musicModel.importPlaylist(uri, pendingImportTarget)
} }
@ -193,7 +193,7 @@ class PlaylistDetailFragment :
getString(R.string.fmt_editing, playlist.name.resolve(requireContext())) getString(R.string.fmt_editing, playlist.name.resolve(requireContext()))
if (editedPlaylist != null) { if (editedPlaylist != null) {
T.d("Binding edited playlist image") L.d("Binding edited playlist image")
binding.detailCover.bind( binding.detailCover.bind(
editedPlaylist, editedPlaylist,
binding.context.getString(R.string.desc_playlist_image, playlist.name), binding.context.getString(R.string.desc_playlist_image, playlist.name),
@ -222,7 +222,7 @@ class PlaylistDetailFragment :
val playable = playlist.songs.isNotEmpty() && editedPlaylist == null val playable = playlist.songs.isNotEmpty() && editedPlaylist == null
if (!playable) { if (!playable) {
T.d("Playlist is being edited or is empty, disabling playback options") L.d("Playlist is being edited or is empty, disabling playback options")
} }
binding.detailPlayButton?.apply { binding.detailPlayButton?.apply {
@ -248,7 +248,7 @@ class PlaylistDetailFragment :
listModel.dropSelection() listModel.dropSelection()
if (editedPlaylist != null) { if (editedPlaylist != null) {
T.d("Updating save button state") L.d("Updating save button state")
requireBinding().detailEditToolbar.menu.findItem(R.id.action_save).apply { requireBinding().detailEditToolbar.menu.findItem(R.id.action_save).apply {
isEnabled = editedPlaylist != detailModel.currentPlaylist.value?.songs isEnabled = editedPlaylist != detailModel.currentPlaylist.value?.songs
} }
@ -260,38 +260,38 @@ class PlaylistDetailFragment :
private fun handleShow(show: Show?) { private fun handleShow(show: Show?) {
when (show) { when (show) {
is Show.SongDetails -> { is Show.SongDetails -> {
T.d("Navigating to ${show.song}") L.d("Navigating to ${show.song}")
findNavController() findNavController()
.navigateSafe(PlaylistDetailFragmentDirections.showSong(show.song.uid)) .navigateSafe(PlaylistDetailFragmentDirections.showSong(show.song.uid))
} }
is Show.SongAlbumDetails -> { is Show.SongAlbumDetails -> {
T.d("Navigating to the album of ${show.song}") L.d("Navigating to the album of ${show.song}")
findNavController() findNavController()
.navigateSafe(PlaylistDetailFragmentDirections.showAlbum(show.song.album.uid)) .navigateSafe(PlaylistDetailFragmentDirections.showAlbum(show.song.album.uid))
} }
is Show.AlbumDetails -> { is Show.AlbumDetails -> {
T.d("Navigating to ${show.album}") L.d("Navigating to ${show.album}")
findNavController() findNavController()
.navigateSafe(PlaylistDetailFragmentDirections.showAlbum(show.album.uid)) .navigateSafe(PlaylistDetailFragmentDirections.showAlbum(show.album.uid))
} }
is Show.ArtistDetails -> { is Show.ArtistDetails -> {
T.d("Navigating to ${show.artist}") L.d("Navigating to ${show.artist}")
findNavController() findNavController()
.navigateSafe(PlaylistDetailFragmentDirections.showArtist(show.artist.uid)) .navigateSafe(PlaylistDetailFragmentDirections.showArtist(show.artist.uid))
} }
is Show.SongArtistDecision -> { is Show.SongArtistDecision -> {
T.d("Navigating to artist choices for ${show.song}") L.d("Navigating to artist choices for ${show.song}")
findNavController() findNavController()
.navigateSafe(PlaylistDetailFragmentDirections.showArtistChoices(show.song.uid)) .navigateSafe(PlaylistDetailFragmentDirections.showArtistChoices(show.song.uid))
} }
is Show.AlbumArtistDecision -> { is Show.AlbumArtistDecision -> {
T.d("Navigating to artist choices for ${show.album}") L.d("Navigating to artist choices for ${show.album}")
findNavController() findNavController()
.navigateSafe( .navigateSafe(
PlaylistDetailFragmentDirections.showArtistChoices(show.album.uid)) PlaylistDetailFragmentDirections.showArtistChoices(show.album.uid))
} }
is Show.PlaylistDetails -> { is Show.PlaylistDetails -> {
T.d("Navigated to this playlist") L.d("Navigated to this playlist")
detailModel.toShow.consume() detailModel.toShow.consume()
} }
is Show.GenreDetails -> { is Show.GenreDetails -> {
@ -332,7 +332,7 @@ class PlaylistDetailFragment :
val directions = val directions =
when (decision) { when (decision) {
is PlaylistDecision.Import -> { is PlaylistDecision.Import -> {
T.d("Importing playlist") L.d("Importing playlist")
pendingImportTarget = decision.target pendingImportTarget = decision.target
requireNotNull(getContentLauncher) { requireNotNull(getContentLauncher) {
"Content picker launcher was not available" "Content picker launcher was not available"
@ -342,7 +342,7 @@ class PlaylistDetailFragment :
return return
} }
is PlaylistDecision.Rename -> { is PlaylistDecision.Rename -> {
T.d("Renaming ${decision.playlist}") L.d("Renaming ${decision.playlist}")
PlaylistDetailFragmentDirections.renamePlaylist( PlaylistDetailFragmentDirections.renamePlaylist(
decision.playlist.uid, decision.playlist.uid,
decision.template, decision.template,
@ -350,15 +350,15 @@ class PlaylistDetailFragment :
decision.reason) decision.reason)
} }
is PlaylistDecision.Export -> { is PlaylistDecision.Export -> {
T.d("Exporting ${decision.playlist}") L.d("Exporting ${decision.playlist}")
PlaylistDetailFragmentDirections.exportPlaylist(decision.playlist.uid) PlaylistDetailFragmentDirections.exportPlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Delete -> { is PlaylistDecision.Delete -> {
T.d("Deleting ${decision.playlist}") L.d("Deleting ${decision.playlist}")
PlaylistDetailFragmentDirections.deletePlaylist(decision.playlist.uid) PlaylistDetailFragmentDirections.deletePlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
T.d("Adding ${decision.songs.size} songs to a playlist") L.d("Adding ${decision.songs.size} songs to a playlist")
PlaylistDetailFragmentDirections.addToPlaylist( PlaylistDetailFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
@ -384,11 +384,11 @@ class PlaylistDetailFragment :
val directions = val directions =
when (decision) { when (decision) {
is PlaybackDecision.PlayFromArtist -> { is PlaybackDecision.PlayFromArtist -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
PlaylistDetailFragmentDirections.playFromArtist(decision.song.uid) PlaylistDetailFragmentDirections.playFromArtist(decision.song.uid)
} }
is PlaybackDecision.PlayFromGenre -> { is PlaybackDecision.PlayFromGenre -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
PlaylistDetailFragmentDirections.playFromGenre(decision.song.uid) PlaylistDetailFragmentDirections.playFromGenre(decision.song.uid)
} }
} }
@ -399,15 +399,15 @@ class PlaylistDetailFragment :
val id = val id =
when { when {
detailModel.editedPlaylist.value != null -> { detailModel.editedPlaylist.value != null -> {
T.d("Currently editing playlist, showing edit toolbar") L.d("Currently editing playlist, showing edit toolbar")
R.id.detail_edit_toolbar R.id.detail_edit_toolbar
} }
listModel.selected.value.isNotEmpty() -> { listModel.selected.value.isNotEmpty() -> {
T.d("Currently selecting, showing selection toolbar") L.d("Currently selecting, showing selection toolbar")
R.id.detail_selection_toolbar R.id.detail_selection_toolbar
} }
else -> { else -> {
T.d("Using normal toolbar") L.d("Using normal toolbar")
R.id.detail_normal_toolbar R.id.detail_normal_toolbar
} }
} }

View file

@ -42,7 +42,7 @@ import org.oxycblt.auxio.playback.replaygain.formatDb
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.concatLocalized import org.oxycblt.auxio.util.concatLocalized
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingMaterialDialogFragment] that shows information about a Song. * A [ViewBindingMaterialDialogFragment] that shows information about a Song.
@ -76,7 +76,7 @@ class SongDetailDialog : ViewBindingMaterialDialogFragment<DialogSongDetailBindi
private fun updateSong(song: Song?, info: AudioProperties?) { private fun updateSong(song: Song?, info: AudioProperties?) {
if (song == null) { if (song == null) {
T.d("No song to show, navigating away") L.d("No song to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -29,7 +29,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.device.DeviceLibrary import org.oxycblt.auxio.music.device.DeviceLibrary
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewModel] that stores choice information for [ShowArtistDialog], and possibly others in the * A [ViewModel] that stores choice information for [ShowArtistDialog], and possibly others in the
@ -59,7 +59,7 @@ class DetailPickerViewModel @Inject constructor(private val musicRepository: Mus
val deviceLibrary = musicRepository.deviceLibrary ?: return val deviceLibrary = musicRepository.deviceLibrary ?: return
// Need to sanitize different items depending on the current set of choices. // Need to sanitize different items depending on the current set of choices.
_artistChoices.value = _artistChoices.value?.sanitize(deviceLibrary) _artistChoices.value = _artistChoices.value?.sanitize(deviceLibrary)
T.d("Updated artist choices: ${_artistChoices.value}") L.d("Updated artist choices: ${_artistChoices.value}")
} }
/** /**
@ -68,20 +68,20 @@ class DetailPickerViewModel @Inject constructor(private val musicRepository: Mus
* @param itemUid The [Music.UID] of the item to show. Must be a [Song] or [Album]. * @param itemUid The [Music.UID] of the item to show. Must be a [Song] or [Album].
*/ */
fun setArtistChoiceUid(itemUid: Music.UID) { fun setArtistChoiceUid(itemUid: Music.UID) {
T.d("Opening navigation choices for $itemUid") L.d("Opening navigation choices for $itemUid")
// Support Songs and Albums, which have parent artists. // Support Songs and Albums, which have parent artists.
_artistChoices.value = _artistChoices.value =
when (val music = musicRepository.find(itemUid)) { when (val music = musicRepository.find(itemUid)) {
is Song -> { is Song -> {
T.d("Creating navigation choices for song") L.d("Creating navigation choices for song")
ArtistShowChoices.FromSong(music) ArtistShowChoices.FromSong(music)
} }
is Album -> { is Album -> {
T.d("Creating navigation choices for album") L.d("Creating navigation choices for album")
ArtistShowChoices.FromAlbum(music) ArtistShowChoices.FromAlbum(music)
} }
else -> { else -> {
T.w("Given song/album UID was invalid") L.w("Given song/album UID was invalid")
null null
} }
} }

View file

@ -35,7 +35,7 @@ import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A picker [ViewBindingMaterialDialogFragment] intended for when the [Artist] to show is ambiguous. * A picker [ViewBindingMaterialDialogFragment] intended for when the [Artist] to show is ambiguous.
@ -85,7 +85,7 @@ class ShowArtistDialog :
private fun updateChoices(choices: ArtistShowChoices?) { private fun updateChoices(choices: ArtistShowChoices?) {
if (choices == null) { if (choices == null) {
T.d("No choices to show, navigating away") L.d("No choices to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -46,7 +46,7 @@ import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.util.context import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.inflater
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [DetailListAdapter] implementing the header, sub-items, and editing state for the [Playlist] * A [DetailListAdapter] implementing the header, sub-items, and editing state for the [Playlist]
@ -97,7 +97,7 @@ class PlaylistDetailListAdapter(private val listener: Listener) :
// Nothing to do. // Nothing to do.
return return
} }
T.d("Updating editing state [old: $isEditing new: $editing]") L.d("Updating editing state [old: $isEditing new: $editing]")
this.isEditing = editing this.isEditing = editing
notifyItemRangeChanged(0, currentList.size, PAYLOAD_EDITING_CHANGED) notifyItemRangeChanged(0, currentList.size, PAYLOAD_EDITING_CHANGED)
} }

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.list.sort.SortDialog import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [SortDialog] that controls the [Sort] of [DetailViewModel.albumSongSort]. * A [SortDialog] that controls the [Sort] of [DetailViewModel.albumSongSort].
@ -56,7 +56,7 @@ class AlbumSongSortDialog : SortDialog() {
private fun updateAlbum(album: Album?) { private fun updateAlbum(album: Album?) {
if (album == null) { if (album == null) {
T.d("No album to sort, navigating away") L.d("No album to sort, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
} }
} }

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.list.sort.SortDialog import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [SortDialog] that controls the [Sort] of [DetailViewModel.artistSongSort]. * A [SortDialog] that controls the [Sort] of [DetailViewModel.artistSongSort].
@ -57,7 +57,7 @@ class ArtistSongSortDialog : SortDialog() {
private fun updateArtist(artist: Artist?) { private fun updateArtist(artist: Artist?) {
if (artist == null) { if (artist == null) {
T.d("No artist to sort, navigating away") L.d("No artist to sort, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
} }
} }

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.list.sort.SortDialog import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [SortDialog] that controls the [Sort] of [DetailViewModel.genreSongSort]. * A [SortDialog] that controls the [Sort] of [DetailViewModel.genreSongSort].
@ -62,7 +62,7 @@ class GenreSongSortDialog : SortDialog() {
private fun updateGenre(genre: Genre?) { private fun updateGenre(genre: Genre?) {
if (genre == null) { if (genre == null) {
T.d("No genre to sort, navigating away") L.d("No genre to sort, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
} }
} }

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.list.sort.SortDialog import org.oxycblt.auxio.list.sort.SortDialog
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [SortDialog] that controls the [Sort] of [DetailViewModel.genreSongSort]. * A [SortDialog] that controls the [Sort] of [DetailViewModel.genreSongSort].
@ -62,7 +62,7 @@ class PlaylistSongSortDialog : SortDialog() {
private fun updatePlaylist(genre: Playlist?) { private fun updatePlaylist(genre: Playlist?) {
if (genre == null) { if (genre == null) {
T.d("No genre to sort, navigating away") L.d("No genre to sort, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
} }
} }

View file

@ -79,7 +79,7 @@ import org.oxycblt.auxio.util.lazyReflectedField
import org.oxycblt.auxio.util.lazyReflectedMethod import org.oxycblt.auxio.util.lazyReflectedMethod
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* The starting [SelectionFragment] of Auxio. Shows the user's music library and enables navigation * The starting [SelectionFragment] of Auxio. Shows the user's music library and enables navigation
@ -125,11 +125,11 @@ class HomeFragment :
getContentLauncher = getContentLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri == null) { if (uri == null) {
T.w("No URI returned from file picker") L.w("No URI returned from file picker")
return@registerForActivityResult return@registerForActivityResult
} }
T.d("Received playlist URI $uri") L.d("Received playlist URI $uri")
musicModel.importPlaylist(uri, pendingImportTarget) musicModel.importPlaylist(uri, pendingImportTarget)
} }
@ -220,17 +220,17 @@ class HomeFragment :
return when (item.itemId) { return when (item.itemId) {
// Handle main actions (Search, Settings, About) // Handle main actions (Search, Settings, About)
R.id.action_search -> { R.id.action_search -> {
T.d("Navigating to search") L.d("Navigating to search")
findNavController().navigateSafe(HomeFragmentDirections.search()) findNavController().navigateSafe(HomeFragmentDirections.search())
true true
} }
R.id.action_settings -> { R.id.action_settings -> {
T.d("Navigating to preferences") L.d("Navigating to preferences")
homeModel.showSettings() homeModel.showSettings()
true true
} }
R.id.action_about -> { R.id.action_about -> {
T.d("Navigating to about") L.d("Navigating to about")
homeModel.showAbout() homeModel.showAbout()
true true
} }
@ -250,7 +250,7 @@ class HomeFragment :
true true
} }
else -> { else -> {
T.w("Unexpected menu item selected") L.w("Unexpected menu item selected")
false false
} }
} }
@ -264,7 +264,7 @@ class HomeFragment :
if (homeModel.currentTabTypes.size == 1) { if (homeModel.currentTabTypes.size == 1) {
// A single tab makes the tab layout redundant, hide it and disable the collapsing // A single tab makes the tab layout redundant, hide it and disable the collapsing
// behavior. // behavior.
T.d("Single tab shown, disabling TabLayout") L.d("Single tab shown, disabling TabLayout")
binding.homeTabs.isVisible = false binding.homeTabs.isVisible = false
binding.homeAppbar.setExpanded(true, false) binding.homeAppbar.setExpanded(true, false)
toolbarParams.scrollFlags = 0 toolbarParams.scrollFlags = 0
@ -302,7 +302,7 @@ class HomeFragment :
private fun handleRecreate(recreate: Unit?) { private fun handleRecreate(recreate: Unit?) {
if (recreate == null) return if (recreate == null) return
val binding = requireBinding() val binding = requireBinding()
T.d("Recreating ViewPager") L.d("Recreating ViewPager")
// Move back to position zero, as there must be a tab there. // Move back to position zero, as there must be a tab there.
binding.homePager.currentItem = 0 binding.homePager.currentItem = 0
// Make sure tabs are set up to also follow the new ViewPager configuration. // Make sure tabs are set up to also follow the new ViewPager configuration.
@ -319,7 +319,7 @@ class HomeFragment :
is IndexingState.Completed -> setupCompleteState(binding, state.error) is IndexingState.Completed -> setupCompleteState(binding, state.error)
is IndexingState.Indexing -> setupIndexingState(binding, state.progress) is IndexingState.Indexing -> setupIndexingState(binding, state.progress)
null -> { null -> {
T.d("Indexer is in indeterminate state") L.d("Indexer is in indeterminate state")
binding.homeIndexingContainer.visibility = View.INVISIBLE binding.homeIndexingContainer.visibility = View.INVISIBLE
} }
} }
@ -327,19 +327,19 @@ class HomeFragment :
private fun setupCompleteState(binding: FragmentHomeBinding, error: Exception?) { private fun setupCompleteState(binding: FragmentHomeBinding, error: Exception?) {
if (error == null) { if (error == null) {
T.d("Received ok response") L.d("Received ok response")
binding.homeIndexingContainer.visibility = View.INVISIBLE binding.homeIndexingContainer.visibility = View.INVISIBLE
return return
} }
T.d("Received non-ok response") L.d("Received non-ok response")
val context = requireContext() val context = requireContext()
binding.homeIndexingContainer.visibility = View.VISIBLE binding.homeIndexingContainer.visibility = View.VISIBLE
binding.homeIndexingProgress.visibility = View.INVISIBLE binding.homeIndexingProgress.visibility = View.INVISIBLE
binding.homeIndexingActions.visibility = View.VISIBLE binding.homeIndexingActions.visibility = View.VISIBLE
when (error) { when (error) {
is NoAudioPermissionException -> { is NoAudioPermissionException -> {
T.d("Showing permission prompt") L.d("Showing permission prompt")
binding.homeIndexingStatus.setText(R.string.err_no_perms) binding.homeIndexingStatus.setText(R.string.err_no_perms)
// Configure the action to act as a permission launcher. // Configure the action to act as a permission launcher.
binding.homeIndexingTry.apply { binding.homeIndexingTry.apply {
@ -354,7 +354,7 @@ class HomeFragment :
binding.homeIndexingMore.visibility = View.GONE binding.homeIndexingMore.visibility = View.GONE
} }
is NoMusicException -> { is NoMusicException -> {
T.d("Showing no music error") L.d("Showing no music error")
binding.homeIndexingStatus.setText(R.string.err_no_music) binding.homeIndexingStatus.setText(R.string.err_no_music)
// Configure the action to act as a reload trigger. // Configure the action to act as a reload trigger.
binding.homeIndexingTry.apply { binding.homeIndexingTry.apply {
@ -365,7 +365,7 @@ class HomeFragment :
binding.homeIndexingMore.visibility = View.GONE binding.homeIndexingMore.visibility = View.GONE
} }
else -> { else -> {
T.d("Showing generic error") L.d("Showing generic error")
binding.homeIndexingStatus.setText(R.string.err_index_failed) binding.homeIndexingStatus.setText(R.string.err_index_failed)
// Configure the action to act as a reload trigger. // Configure the action to act as a reload trigger.
binding.homeIndexingTry.apply { binding.homeIndexingTry.apply {
@ -411,14 +411,14 @@ class HomeFragment :
val directions = val directions =
when (decision) { when (decision) {
is PlaylistDecision.New -> { is PlaylistDecision.New -> {
T.d("Creating new playlist") L.d("Creating new playlist")
HomeFragmentDirections.newPlaylist( HomeFragmentDirections.newPlaylist(
decision.songs.map { it.uid }.toTypedArray(), decision.songs.map { it.uid }.toTypedArray(),
decision.template, decision.template,
decision.reason) decision.reason)
} }
is PlaylistDecision.Import -> { is PlaylistDecision.Import -> {
T.d("Importing playlist") L.d("Importing playlist")
pendingImportTarget = decision.target pendingImportTarget = decision.target
requireNotNull(getContentLauncher) { requireNotNull(getContentLauncher) {
"Content picker launcher was not available" "Content picker launcher was not available"
@ -428,7 +428,7 @@ class HomeFragment :
return return
} }
is PlaylistDecision.Rename -> { is PlaylistDecision.Rename -> {
T.d("Renaming ${decision.playlist}") L.d("Renaming ${decision.playlist}")
HomeFragmentDirections.renamePlaylist( HomeFragmentDirections.renamePlaylist(
decision.playlist.uid, decision.playlist.uid,
decision.template, decision.template,
@ -436,15 +436,15 @@ class HomeFragment :
decision.reason) decision.reason)
} }
is PlaylistDecision.Export -> { is PlaylistDecision.Export -> {
T.d("Exporting ${decision.playlist}") L.d("Exporting ${decision.playlist}")
HomeFragmentDirections.exportPlaylist(decision.playlist.uid) HomeFragmentDirections.exportPlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Delete -> { is PlaylistDecision.Delete -> {
T.d("Deleting ${decision.playlist}") L.d("Deleting ${decision.playlist}")
HomeFragmentDirections.deletePlaylist(decision.playlist.uid) HomeFragmentDirections.deletePlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
T.d("Adding ${decision.songs.size} to a playlist") L.d("Adding ${decision.songs.size} to a playlist")
HomeFragmentDirections.addToPlaylist( HomeFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
@ -475,38 +475,38 @@ class HomeFragment :
private fun handleShow(show: Show?) { private fun handleShow(show: Show?) {
when (show) { when (show) {
is Show.SongDetails -> { is Show.SongDetails -> {
T.d("Navigating to ${show.song}") L.d("Navigating to ${show.song}")
findNavController().navigateSafe(HomeFragmentDirections.showSong(show.song.uid)) findNavController().navigateSafe(HomeFragmentDirections.showSong(show.song.uid))
} }
is Show.SongAlbumDetails -> { is Show.SongAlbumDetails -> {
T.d("Navigating to the album of ${show.song}") L.d("Navigating to the album of ${show.song}")
findNavController() findNavController()
.navigateSafe(HomeFragmentDirections.showAlbum(show.song.album.uid)) .navigateSafe(HomeFragmentDirections.showAlbum(show.song.album.uid))
} }
is Show.AlbumDetails -> { is Show.AlbumDetails -> {
T.d("Navigating to ${show.album}") L.d("Navigating to ${show.album}")
findNavController().navigateSafe(HomeFragmentDirections.showAlbum(show.album.uid)) findNavController().navigateSafe(HomeFragmentDirections.showAlbum(show.album.uid))
} }
is Show.ArtistDetails -> { is Show.ArtistDetails -> {
T.d("Navigating to ${show.artist}") L.d("Navigating to ${show.artist}")
findNavController().navigateSafe(HomeFragmentDirections.showArtist(show.artist.uid)) findNavController().navigateSafe(HomeFragmentDirections.showArtist(show.artist.uid))
} }
is Show.SongArtistDecision -> { is Show.SongArtistDecision -> {
T.d("Navigating to artist choices for ${show.song}") L.d("Navigating to artist choices for ${show.song}")
findNavController() findNavController()
.navigateSafe(HomeFragmentDirections.showArtistChoices(show.song.uid)) .navigateSafe(HomeFragmentDirections.showArtistChoices(show.song.uid))
} }
is Show.AlbumArtistDecision -> { is Show.AlbumArtistDecision -> {
T.d("Navigating to artist choices for ${show.album}") L.d("Navigating to artist choices for ${show.album}")
findNavController() findNavController()
.navigateSafe(HomeFragmentDirections.showArtistChoices(show.album.uid)) .navigateSafe(HomeFragmentDirections.showArtistChoices(show.album.uid))
} }
is Show.GenreDetails -> { is Show.GenreDetails -> {
T.d("Navigating to ${show.genre}") L.d("Navigating to ${show.genre}")
findNavController().navigateSafe(HomeFragmentDirections.showGenre(show.genre.uid)) findNavController().navigateSafe(HomeFragmentDirections.showGenre(show.genre.uid))
} }
is Show.PlaylistDetails -> { is Show.PlaylistDetails -> {
T.d("Navigating to ${show.playlist}") L.d("Navigating to ${show.playlist}")
findNavController() findNavController()
.navigateSafe(HomeFragmentDirections.showPlaylist(show.playlist.uid)) .navigateSafe(HomeFragmentDirections.showPlaylist(show.playlist.uid))
} }
@ -534,7 +534,7 @@ class HomeFragment :
binding.homeSelectionToolbar.title = getString(R.string.fmt_selected, selected.size) binding.homeSelectionToolbar.title = getString(R.string.fmt_selected, selected.size)
if (binding.homeToolbar.setVisible(R.id.home_selection_toolbar)) { if (binding.homeToolbar.setVisible(R.id.home_selection_toolbar)) {
// New selection started, show the AppBarLayout to indicate the new state. // New selection started, show the AppBarLayout to indicate the new state.
T.d("Significant selection occurred, expanding AppBar") L.d("Significant selection occurred, expanding AppBar")
binding.homeAppbar.expandWithScrollingRecycler() binding.homeAppbar.expandWithScrollingRecycler()
} }
} else { } else {

View file

@ -29,7 +29,7 @@ import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import timber.log.Timber as T import timber.log.Timber as L
interface HomeGenerator { interface HomeGenerator {
fun attach() fun attach()
@ -89,7 +89,7 @@ private class HomeGeneratorImpl(
override fun onHideCollaboratorsChanged() { override fun onHideCollaboratorsChanged() {
// Changes in the hide collaborator setting will change the artist contents // Changes in the hide collaborator setting will change the artist contents
// of the library, consider it a library update. // of the library, consider it a library update.
T.d("Collaborator setting changed, forwarding update") L.d("Collaborator setting changed, forwarding update")
onMusicChanges(MusicRepository.Changes(deviceLibrary = true, userLibrary = false)) onMusicChanges(MusicRepository.Changes(deviceLibrary = true, userLibrary = false))
} }
@ -121,7 +121,7 @@ private class HomeGeneratorImpl(
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
val deviceLibrary = musicRepository.deviceLibrary val deviceLibrary = musicRepository.deviceLibrary
if (changes.deviceLibrary && deviceLibrary != null) { if (changes.deviceLibrary && deviceLibrary != null) {
T.d("Refreshing library") L.d("Refreshing library")
// Get the each list of items in the library to use as our list data. // Get the each list of items in the library to use as our list data.
// Applying the preferred sorting to them. // Applying the preferred sorting to them.
invalidator.invalidateMusic(MusicType.SONGS, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.SONGS, UpdateInstructions.Diff)
@ -132,7 +132,7 @@ private class HomeGeneratorImpl(
val userLibrary = musicRepository.userLibrary val userLibrary = musicRepository.userLibrary
if (changes.userLibrary && userLibrary != null) { if (changes.userLibrary && userLibrary != null) {
T.d("Refreshing playlists") L.d("Refreshing playlists")
invalidator.invalidateMusic(MusicType.PLAYLISTS, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.PLAYLISTS, UpdateInstructions.Diff)
} }
} }

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* User configuration specific to the home UI. * User configuration specific to the home UI.
@ -68,17 +68,17 @@ class HomeSettingsImpl @Inject constructor(@ApplicationContext context: Context)
override fun migrate() { override fun migrate() {
if (sharedPreferences.contains(OLD_KEY_LIB_TABS)) { if (sharedPreferences.contains(OLD_KEY_LIB_TABS)) {
T.d("Migrating tab setting") L.d("Migrating tab setting")
val oldTabs = val oldTabs =
Tab.fromIntCode(sharedPreferences.getInt(OLD_KEY_LIB_TABS, Tab.SEQUENCE_DEFAULT)) Tab.fromIntCode(sharedPreferences.getInt(OLD_KEY_LIB_TABS, Tab.SEQUENCE_DEFAULT))
?: unlikelyToBeNull(Tab.fromIntCode(Tab.SEQUENCE_DEFAULT)) ?: unlikelyToBeNull(Tab.fromIntCode(Tab.SEQUENCE_DEFAULT))
T.d("Old tabs: $oldTabs") L.d("Old tabs: $oldTabs")
// The playlist tab is now parsed, but it needs to be made visible. // The playlist tab is now parsed, but it needs to be made visible.
val playlistIndex = oldTabs.indexOfFirst { it.type == MusicType.PLAYLISTS } val playlistIndex = oldTabs.indexOfFirst { it.type == MusicType.PLAYLISTS }
check(playlistIndex > -1) // This should exist, otherwise we are in big trouble check(playlistIndex > -1) // This should exist, otherwise we are in big trouble
oldTabs[playlistIndex] = Tab.Visible(MusicType.PLAYLISTS) oldTabs[playlistIndex] = Tab.Visible(MusicType.PLAYLISTS)
T.d("New tabs: $oldTabs") L.d("New tabs: $oldTabs")
sharedPreferences.edit { sharedPreferences.edit {
putInt(getString(R.string.set_key_home_tabs), Tab.toIntCode(oldTabs)) putInt(getString(R.string.set_key_home_tabs), Tab.toIntCode(oldTabs))
@ -90,11 +90,11 @@ class HomeSettingsImpl @Inject constructor(@ApplicationContext context: Context)
override fun onSettingChanged(key: String, listener: HomeSettings.Listener) { override fun onSettingChanged(key: String, listener: HomeSettings.Listener) {
when (key) { when (key) {
getString(R.string.set_key_home_tabs) -> { getString(R.string.set_key_home_tabs) -> {
T.d("Dispatching tab setting change") L.d("Dispatching tab setting change")
listener.onTabsChanged() listener.onTabsChanged()
} }
getString(R.string.set_key_hide_collaborators) -> { getString(R.string.set_key_hide_collaborators) -> {
T.d("Dispatching collaborator setting change") L.d("Dispatching collaborator setting change")
listener.onHideCollaboratorsChanged() listener.onHideCollaboratorsChanged()
} }
} }

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.playback.PlaySong
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.util.Event import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent import org.oxycblt.auxio.util.MutableEvent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* The ViewModel for managing the tab data and lists of the home view. * The ViewModel for managing the tab data and lists of the home view.
@ -249,7 +249,7 @@ constructor(
* @param pagerPos The new position of the ViewPager2 instance. * @param pagerPos The new position of the ViewPager2 instance.
*/ */
fun synchronizeTabPosition(pagerPos: Int) { fun synchronizeTabPosition(pagerPos: Int) {
T.d("Updating current tab to ${currentTabTypes[pagerPos]}") L.d("Updating current tab to ${currentTabTypes[pagerPos]}")
_currentTabType.value = currentTabTypes[pagerPos] _currentTabType.value = currentTabTypes[pagerPos]
} }
@ -259,7 +259,7 @@ constructor(
* @param isFastScrolling true if the user is currently fast scrolling, false otherwise. * @param isFastScrolling true if the user is currently fast scrolling, false otherwise.
*/ */
fun setFastScrolling(isFastScrolling: Boolean) { fun setFastScrolling(isFastScrolling: Boolean) {
T.d("Updating fast scrolling state: $isFastScrolling") L.d("Updating fast scrolling state: $isFastScrolling")
_isFastScrolling.value = isFastScrolling _isFastScrolling.value = isFastScrolling
} }

View file

@ -39,9 +39,6 @@ import androidx.core.os.BundleCompat
import androidx.core.view.setMargins import androidx.core.view.setMargins
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.material.R as MR
import com.google.android.material.motion.MotionUtils
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.leinardi.android.speeddial.FabWithLabelView import com.leinardi.android.speeddial.FabWithLabelView
import com.leinardi.android.speeddial.SpeedDialActionItem import com.leinardi.android.speeddial.SpeedDialActionItem
@ -49,6 +46,7 @@ import com.leinardi.android.speeddial.SpeedDialView
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.ui.StationaryAnim
import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.getDimen import org.oxycblt.auxio.util.getDimen
import org.oxycblt.auxio.util.getDimenPixels import org.oxycblt.auxio.util.getDimenPixels
@ -80,12 +78,7 @@ class ThemedSpeedDialView : SpeedDialView {
@AttrRes defStyleAttr: Int @AttrRes defStyleAttr: Int
) : super(context, attrs, defStyleAttr) ) : super(context, attrs, defStyleAttr)
private val matInterpolator = private val inAnim = StationaryAnim.forMediumComponent(context)
MotionUtils.resolveThemeInterpolator(
context, MR.attr.motionEasingStandardInterpolator, FastOutSlowInInterpolator())
private val matDuration =
MotionUtils.resolveThemeDuration(context, MR.attr.motionDurationMedium2, 350)
init { init {
// Work around ripple bug on Android 12 when useCompatPadding = true. // Work around ripple bug on Android 12 when useCompatPadding = true.
@ -149,7 +142,7 @@ class ThemedSpeedDialView : SpeedDialView {
} }
private fun createMainFabAnimator(isOpen: Boolean): Animator { private fun createMainFabAnimator(isOpen: Boolean): Animator {
val totalDuration = matDuration.toLong() val totalDuration = inAnim.duration
val partialDuration = totalDuration / 2 // This is half of the total duration val partialDuration = totalDuration / 2 // This is half of the total duration
val delay = totalDuration / 4 // This is one fourth of the total duration val delay = totalDuration / 4 // This is one fourth of the total duration
@ -181,7 +174,7 @@ class ThemedSpeedDialView : SpeedDialView {
val animatorSet = val animatorSet =
AnimatorSet().apply { AnimatorSet().apply {
playTogether(backgroundTintAnimator, imageTintAnimator, levelAnimator) playTogether(backgroundTintAnimator, imageTintAnimator, levelAnimator)
interpolator = matInterpolator interpolator = inAnim.interpolator
} }
animatorSet.start() animatorSet.start()
return animatorSet return animatorSet

View file

@ -31,15 +31,14 @@ import android.view.WindowInsets
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.R as MR
import com.google.android.material.motion.MotionUtils
import kotlin.math.abs import kotlin.math.abs
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.recycler.AuxioRecyclerView import org.oxycblt.auxio.list.recycler.AuxioRecyclerView
import org.oxycblt.auxio.ui.InAnim
import org.oxycblt.auxio.ui.OutAnim
import org.oxycblt.auxio.util.getDimenPixels import org.oxycblt.auxio.util.getDimenPixels
import org.oxycblt.auxio.util.getDrawableCompat import org.oxycblt.auxio.util.getDrawableCompat
import org.oxycblt.auxio.util.isRtl import org.oxycblt.auxio.util.isRtl
@ -85,23 +84,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
background = context.getDrawableCompat(R.drawable.ui_scroll_thumb) background = context.getDrawableCompat(R.drawable.ui_scroll_thumb)
} }
private val thumbEnterInterpolator = private val thumbEnter = InAnim.forSmallComponent(context)
MotionUtils.resolveThemeInterpolator( private val thumbExit = OutAnim.forSmallComponent(context)
context,
MR.attr.motionEasingEmphasizedDecelerateInterpolator,
FastOutSlowInInterpolator())
private val thumbEnterDuration =
MotionUtils.resolveThemeDuration(context, MR.attr.motionDurationShort4, 300)
private val thumbExitInterpolator =
MotionUtils.resolveThemeInterpolator(
context,
MR.attr.motionEasingEmphasizedAccelerateInterpolator,
FastOutSlowInInterpolator())
private val thumbExitDuration =
MotionUtils.resolveThemeDuration(context, MR.attr.motionDurationShort2, 100)
private val thumbWidth = thumbView.background.intrinsicWidth private val thumbWidth = thumbView.background.intrinsicWidth
private val thumbHeight = thumbView.background.intrinsicHeight private val thumbHeight = thumbView.background.intrinsicHeight
@ -130,23 +114,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
} }
} }
private val popupEnterInterpolator = private val popupEnter = InAnim.forSmallComponent(context)
MotionUtils.resolveThemeInterpolator( private val popupExit = OutAnim.forSmallComponent(context)
context,
MR.attr.motionEasingEmphasizedDecelerateInterpolator,
FastOutSlowInInterpolator())
private val popupEnterDuration =
MotionUtils.resolveThemeDuration(context, MR.attr.motionDurationMedium1, 300)
private val popupExitInterpolator =
MotionUtils.resolveThemeInterpolator(
context,
MR.attr.motionEasingEmphasizedAccelerateInterpolator,
FastOutSlowInInterpolator())
private val popupExitDuration =
MotionUtils.resolveThemeDuration(context, MR.attr.motionDurationShort2, 100)
private var showingPopup = false private var showingPopup = false
@ -460,8 +429,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
thumbView thumbView
.animate() .animate()
.scaleX(1f) .scaleX(1f)
.setInterpolator(thumbEnterInterpolator) .setInterpolator(thumbEnter.interpolator)
.setDuration(thumbEnterDuration.toLong()) .setDuration(thumbEnter.duration)
.start() .start()
} }
@ -474,8 +443,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
thumbView thumbView
.animate() .animate()
.scaleX(0f) .scaleX(0f)
.setInterpolator(thumbExitInterpolator) .setInterpolator(thumbExit.interpolator)
.setDuration(thumbExitDuration.toLong()) .setDuration(thumbExit.duration)
.start() .start()
} }
@ -493,8 +462,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
.animate() .animate()
.scaleX(1f) .scaleX(1f)
.scaleY(1f) .scaleY(1f)
.setInterpolator(popupEnterInterpolator) .setInterpolator(popupEnter.interpolator)
.setDuration(popupEnterDuration.toLong()) .setDuration(popupEnter.duration)
.start() .start()
} }
@ -509,8 +478,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
.alpha(0f) .alpha(0f)
.scaleX(0.75f) .scaleX(0.75f)
.scaleY(0.75f) .scaleY(0.75f)
.setInterpolator(popupExitInterpolator) .setInterpolator(popupExit.interpolator)
.setDuration(popupExitDuration.toLong()) .setDuration(popupExit.duration)
.start() .start()
} }

View file

@ -19,7 +19,7 @@
package org.oxycblt.auxio.home.tabs package org.oxycblt.auxio.home.tabs
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A representation of a library tab suitable for configuration. * A representation of a library tab suitable for configuration.
@ -85,7 +85,7 @@ sealed class Tab(open val type: MusicType) {
// Like when deserializing, make sure there are no duplicate tabs for whatever reason. // Like when deserializing, make sure there are no duplicate tabs for whatever reason.
val distinct = tabs.distinctBy { it.type } val distinct = tabs.distinctBy { it.type }
if (tabs.size != distinct.size) { if (tabs.size != distinct.size) {
T.w( L.w(
"Tab sequences should not have duplicates [old: ${tabs.size} new: ${distinct.size}]") "Tab sequences should not have duplicates [old: ${tabs.size} new: ${distinct.size}]")
} }
@ -132,13 +132,13 @@ sealed class Tab(open val type: MusicType) {
// Make sure there are no duplicate tabs // Make sure there are no duplicate tabs
val distinct = tabs.distinctBy { it.type } val distinct = tabs.distinctBy { it.type }
if (tabs.size != distinct.size) { if (tabs.size != distinct.size) {
T.w( L.w(
"Tab sequences should not have duplicates [old: ${tabs.size} new: ${distinct.size}]") "Tab sequences should not have duplicates [old: ${tabs.size} new: ${distinct.size}]")
} }
// For safety, return null if we have an empty or larger-than-expected tab array. // For safety, return null if we have an empty or larger-than-expected tab array.
if (distinct.isEmpty() || distinct.size < MAX_SEQUENCE_IDX) { if (distinct.isEmpty() || distinct.size < MAX_SEQUENCE_IDX) {
T.e("Sequence size was ${distinct.size}, which is invalid") L.e("Sequence size was ${distinct.size}, which is invalid")
return null return null
} }

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.list.EditClickListListener
import org.oxycblt.auxio.list.recycler.DialogRecyclerView import org.oxycblt.auxio.list.recycler.DialogRecyclerView
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.inflater
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [RecyclerView.Adapter] that displays an array of [Tab]s open for configuration. * A [RecyclerView.Adapter] that displays an array of [Tab]s open for configuration.
@ -55,7 +55,7 @@ class TabAdapter(private val listener: EditClickListListener<Tab>) :
* @param newTabs The new array of tabs to show. * @param newTabs The new array of tabs to show.
*/ */
fun submitTabs(newTabs: Array<Tab>) { fun submitTabs(newTabs: Array<Tab>) {
T.d("Force-updating tab information") L.d("Force-updating tab information")
tabs = newTabs tabs = newTabs
@Suppress("NotifyDatasetChanged") notifyDataSetChanged() @Suppress("NotifyDatasetChanged") notifyDataSetChanged()
} }
@ -67,7 +67,7 @@ class TabAdapter(private val listener: EditClickListListener<Tab>) :
* @param tab The new tab. * @param tab The new tab.
*/ */
fun setTab(at: Int, tab: Tab) { fun setTab(at: Int, tab: Tab) {
T.d("Updating tab [at: $at, tab: $tab]") L.d("Updating tab [at: $at, tab: $tab]")
tabs[at] = tab tabs[at] = tab
// Use a payload to avoid an item change animation. // Use a payload to avoid an item change animation.
notifyItemChanged(at, PAYLOAD_TAB_CHANGED) notifyItemChanged(at, PAYLOAD_TAB_CHANGED)
@ -80,7 +80,7 @@ class TabAdapter(private val listener: EditClickListListener<Tab>) :
* @param b The position of the second tab to swap. * @param b The position of the second tab to swap.
*/ */
fun swapTabs(a: Int, b: Int) { fun swapTabs(a: Int, b: Int) {
T.d("Swapping tabs [a: $a, b: $b]") L.d("Swapping tabs [a: $a, b: $b]")
val tmp = tabs[b] val tmp = tabs[b]
tabs[b] = tabs[a] tabs[b] = tabs[a]
tabs[a] = tmp tabs[a] = tmp

View file

@ -31,7 +31,7 @@ import org.oxycblt.auxio.databinding.DialogTabsBinding
import org.oxycblt.auxio.home.HomeSettings import org.oxycblt.auxio.home.HomeSettings
import org.oxycblt.auxio.list.EditClickListListener import org.oxycblt.auxio.list.EditClickListListener
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingMaterialDialogFragment] that allows the user to modify the home [Tab] * A [ViewBindingMaterialDialogFragment] that allows the user to modify the home [Tab]
@ -52,7 +52,7 @@ class TabCustomizeDialog :
builder builder
.setTitle(R.string.set_lib_tabs) .setTitle(R.string.set_lib_tabs)
.setPositiveButton(R.string.lbl_ok) { _, _ -> .setPositiveButton(R.string.lbl_ok) { _, _ ->
T.d("Committing tab changes") L.d("Committing tab changes")
homeSettings.homeTabs = tabAdapter.tabs homeSettings.homeTabs = tabAdapter.tabs
} }
.setNegativeButton(R.string.lbl_cancel, null) .setNegativeButton(R.string.lbl_cancel, null)
@ -99,7 +99,7 @@ class TabCustomizeDialog :
is Tab.Visible -> Tab.Invisible(old.type) is Tab.Visible -> Tab.Invisible(old.type)
is Tab.Invisible -> Tab.Visible(old.type) is Tab.Invisible -> Tab.Visible(old.type)
} }
T.d("Flipping tab visibility [from: $old to: $new]") L.d("Flipping tab visibility [from: $old to: $new]")
tabAdapter.setTab(index, new) tabAdapter.setTab(index, new)
// Prevent the user from saving if all the tabs are Invisible, as that's an invalid state. // Prevent the user from saving if all the tabs are Invisible, as that's an invalid state.

View file

@ -24,7 +24,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* User configuration specific to image loading. * User configuration specific to image loading.
@ -58,7 +58,7 @@ class ImageSettingsImpl @Inject constructor(@ApplicationContext context: Context
// Show album covers and Ignore MediaStore covers were unified in 3.0.0 // Show album covers and Ignore MediaStore covers were unified in 3.0.0
if (sharedPreferences.contains(OLD_KEY_SHOW_COVERS) || if (sharedPreferences.contains(OLD_KEY_SHOW_COVERS) ||
sharedPreferences.contains(OLD_KEY_QUALITY_COVERS)) { sharedPreferences.contains(OLD_KEY_QUALITY_COVERS)) {
T.d("Migrating cover settings") L.d("Migrating cover settings")
val mode = val mode =
when { when {
@ -79,7 +79,7 @@ class ImageSettingsImpl @Inject constructor(@ApplicationContext context: Context
override fun onSettingChanged(key: String, listener: ImageSettings.Listener) { override fun onSettingChanged(key: String, listener: ImageSettings.Listener) {
if (key == getString(R.string.set_key_cover_mode) || if (key == getString(R.string.set_key_cover_mode) ||
key == getString(R.string.set_key_square_covers)) { key == getString(R.string.set_key_square_covers)) {
T.d("Dispatching image setting change") L.d("Dispatching image setting change")
listener.onImageSettingsChanged() listener.onImageSettingsChanged()
} }
} }

View file

@ -53,7 +53,7 @@ import okio.source
import org.oxycblt.auxio.image.CoverMode import org.oxycblt.auxio.image.CoverMode
import org.oxycblt.auxio.image.ImageSettings import org.oxycblt.auxio.image.ImageSettings
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Provides functionality for extracting album cover information. Meant for internal use only. * Provides functionality for extracting album cover information. Meant for internal use only.
@ -153,13 +153,14 @@ constructor(
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to extract album cover due to an error: $e") L.e("Unable to extract album cover due to an error: $e")
null null
} }
private suspend fun extractQualityCover(cover: Cover.Embedded) = private suspend fun extractQualityCover(cover: Cover.Embedded) =
extractExoplayerCover(cover) extractExoplayerCover(cover)
?: extractAospMetadataCover(cover) ?: extractMediaStoreCover(cover) ?: extractAospMetadataCover(cover)
?: extractMediaStoreCover(cover)
private fun extractAospMetadataCover(cover: Cover.Embedded): InputStream? = private fun extractAospMetadataCover(cover: Cover.Embedded): InputStream? =
MediaMetadataRetriever().run { MediaMetadataRetriever().run {

View file

@ -36,7 +36,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaySong import org.oxycblt.auxio.playback.PlaySong
import org.oxycblt.auxio.util.Event import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent import org.oxycblt.auxio.util.MutableEvent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewModel] that orchestrates menu dialogs and selection state. * A [ViewModel] that orchestrates menu dialogs and selection state.
@ -93,16 +93,16 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
*/ */
fun select(music: Music) { fun select(music: Music) {
if (music is MusicParent && music.songs.isEmpty()) { if (music is MusicParent && music.songs.isEmpty()) {
T.d("Cannot select empty parent, ignoring operation") L.d("Cannot select empty parent, ignoring operation")
return return
} }
val selected = _selected.value.toMutableList() val selected = _selected.value.toMutableList()
if (!selected.remove(music)) { if (!selected.remove(music)) {
T.d("Adding $music to selection") L.d("Adding $music to selection")
selected.add(music) selected.add(music)
} else { } else {
T.d("Removed $music from selection") L.d("Removed $music from selection")
} }
_selected.value = selected _selected.value = selected
@ -130,7 +130,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @return A list of [Song]s collated from each item selected. * @return A list of [Song]s collated from each item selected.
*/ */
fun takeSelection(): List<Song> { fun takeSelection(): List<Song> {
T.d("Taking selection") L.d("Taking selection")
return peekSelection().also { _selected.value = listOf() } return peekSelection().also { _selected.value = listOf() }
} }
@ -140,7 +140,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @return true if the prior selection was non-empty, false otherwise. * @return true if the prior selection was non-empty, false otherwise.
*/ */
fun dropSelection(): Boolean { fun dropSelection(): Boolean {
T.d("Dropping selection [empty=${_selected.value.isEmpty()}]") L.d("Dropping selection [empty=${_selected.value.isEmpty()}]")
return _selected.value.isNotEmpty().also { _selected.value = listOf() } return _selected.value.isNotEmpty().also { _selected.value = listOf() }
} }
@ -154,7 +154,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* should do. * should do.
*/ */
fun openMenu(@MenuRes menuRes: Int, song: Song, playWith: PlaySong) { fun openMenu(@MenuRes menuRes: Int, song: Song, playWith: PlaySong) {
T.d("Opening menu for $song") L.d("Opening menu for $song")
openImpl(Menu.ForSong(menuRes, song, playWith)) openImpl(Menu.ForSong(menuRes, song, playWith))
} }
@ -166,7 +166,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @param album The [Album] to show. * @param album The [Album] to show.
*/ */
fun openMenu(@MenuRes menuRes: Int, album: Album) { fun openMenu(@MenuRes menuRes: Int, album: Album) {
T.d("Opening menu for $album") L.d("Opening menu for $album")
openImpl(Menu.ForAlbum(menuRes, album)) openImpl(Menu.ForAlbum(menuRes, album))
} }
@ -178,7 +178,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @param artist The [Artist] to show. * @param artist The [Artist] to show.
*/ */
fun openMenu(@MenuRes menuRes: Int, artist: Artist) { fun openMenu(@MenuRes menuRes: Int, artist: Artist) {
T.d("Opening menu for $artist") L.d("Opening menu for $artist")
openImpl(Menu.ForArtist(menuRes, artist)) openImpl(Menu.ForArtist(menuRes, artist))
} }
@ -190,7 +190,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @param genre The [Genre] to show. * @param genre The [Genre] to show.
*/ */
fun openMenu(@MenuRes menuRes: Int, genre: Genre) { fun openMenu(@MenuRes menuRes: Int, genre: Genre) {
T.d("Opening menu for $genre") L.d("Opening menu for $genre")
openImpl(Menu.ForGenre(menuRes, genre)) openImpl(Menu.ForGenre(menuRes, genre))
} }
@ -202,7 +202,7 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @param playlist The [Playlist] to show. * @param playlist The [Playlist] to show.
*/ */
fun openMenu(@MenuRes menuRes: Int, playlist: Playlist) { fun openMenu(@MenuRes menuRes: Int, playlist: Playlist) {
T.d("Opening menu for $playlist") L.d("Opening menu for $playlist")
openImpl(Menu.ForPlaylist(menuRes, playlist)) openImpl(Menu.ForPlaylist(menuRes, playlist))
} }
@ -214,14 +214,14 @@ constructor(private val listSettings: ListSettings, private val musicRepository:
* @param songs The [Song] selection to show. * @param songs The [Song] selection to show.
*/ */
fun openMenu(@MenuRes menuRes: Int, songs: List<Song>) { fun openMenu(@MenuRes menuRes: Int, songs: List<Song>) {
T.d("Opening menu for ${songs.size} songs") L.d("Opening menu for ${songs.size} songs")
openImpl(Menu.ForSelection(menuRes, songs)) openImpl(Menu.ForSelection(menuRes, songs))
} }
private fun openImpl(menu: Menu) { private fun openImpl(menu: Menu) {
val existing = _menu.flow.value val existing = _menu.flow.value
if (existing != null) { if (existing != null) {
T.w("Already opening $existing, ignoring $menu") L.w("Already opening $existing, ignoring $menu")
return return
} }
_menu.put(menu) _menu.put(menu)

View file

@ -25,6 +25,7 @@ import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import java.util.concurrent.Executor import java.util.concurrent.Executor
import timber.log.Timber as L
/** /**
* A variant of ListDiffer with more flexible updates. * A variant of ListDiffer with more flexible updates.
@ -56,7 +57,7 @@ abstract class FlexibleListAdapter<T, VH : RecyclerView.ViewHolder>(
instructions: UpdateInstructions?, instructions: UpdateInstructions?,
callback: (() -> Unit)? = null callback: (() -> Unit)? = null
) { ) {
T.d("Updating list to ${newList.size} items with $instructions") L.d("Updating list to ${newList.size} items with $instructions")
differ.update(newList, instructions, callback) differ.update(newList, instructions, callback)
} }
} }
@ -170,7 +171,7 @@ private class FlexibleListDiffer<T>(
) { ) {
// fast simple remove all // fast simple remove all
if (newList.isEmpty()) { if (newList.isEmpty()) {
T.d("Short-circuiting diff to remove all") L.d("Short-circuiting diff to remove all")
val countRemoved = oldList.size val countRemoved = oldList.size
currentList = emptyList() currentList = emptyList()
// notify last, after list is updated // notify last, after list is updated
@ -181,7 +182,7 @@ private class FlexibleListDiffer<T>(
// fast simple first insert // fast simple first insert
if (oldList.isEmpty()) { if (oldList.isEmpty()) {
T.d("Short-circuiting diff to insert all") L.d("Short-circuiting diff to insert all")
currentList = newList currentList = newList
// notify last, after list is updated // notify last, after list is updated
updateCallback.onInserted(0, newList.size) updateCallback.onInserted(0, newList.size)
@ -243,7 +244,7 @@ private class FlexibleListDiffer<T>(
mainThreadExecutor.execute { mainThreadExecutor.execute {
if (maxScheduledGeneration == runGeneration) { if (maxScheduledGeneration == runGeneration) {
T.d("Applying calculated diff") L.d("Applying calculated diff")
currentList = newList currentList = newList
result.dispatchUpdatesTo(updateCallback) result.dispatchUpdatesTo(updateCallback)
callback?.invoke() callback?.invoke()

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.list.adapter
import android.view.View import android.view.View
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import timber.log.Timber as L
/** /**
* A [RecyclerView.Adapter] that supports indicating the playback status of a particular item. * A [RecyclerView.Adapter] that supports indicating the playback status of a particular item.
@ -57,7 +58,7 @@ abstract class PlayingIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
* @param isPlaying Whether playback is ongoing or paused. * @param isPlaying Whether playback is ongoing or paused.
*/ */
fun setPlaying(item: T?, isPlaying: Boolean) { fun setPlaying(item: T?, isPlaying: Boolean) {
T.d("Updating playing item [old: $currentItem new: $item]") L.d("Updating playing item [old: $currentItem new: $item]")
var updatedItem = false var updatedItem = false
if (currentItem != item) { if (currentItem != item) {
@ -70,7 +71,7 @@ abstract class PlayingIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
if (pos > -1) { if (pos > -1) {
notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED) notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED)
} else { } else {
T.w("oldItem was not in adapter data") L.w("oldItem was not in adapter data")
} }
} }
@ -80,7 +81,7 @@ abstract class PlayingIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
if (pos > -1) { if (pos > -1) {
notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED) notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED)
} else { } else {
T.w("newItem was not in adapter data") L.w("newItem was not in adapter data")
} }
} }
@ -98,7 +99,7 @@ abstract class PlayingIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
if (pos > -1) { if (pos > -1) {
notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED) notifyItemChanged(pos, PAYLOAD_PLAYING_INDICATOR_CHANGED)
} else { } else {
T.w("newItem was not in adapter data") L.w("newItem was not in adapter data")
} }
} }
} }

View file

@ -22,6 +22,7 @@ import android.view.View
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import timber.log.Timber as L
/** /**
* A [PlayingIndicatorAdapter] that also supports indicating the selection status of a group of * A [PlayingIndicatorAdapter] that also supports indicating the selection status of a group of
@ -54,7 +55,7 @@ abstract class SelectionIndicatorAdapter<T, VH : RecyclerView.ViewHolder>(
// Nothing to do. // Nothing to do.
return return
} }
T.d("Updating selection [old=${oldSelectedItems.size} new=${newSelectedItems.size}") L.d("Updating selection [old=${oldSelectedItems.size} new=${newSelectedItems.size}")
selectedItems = newSelectedItems selectedItems = newSelectedItems
for (i in currentList.indices) { for (i in currentList.indices) {

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.list.ListViewModel
import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingBottomSheetDialogFragment] that displays basic music information and a series of * A [ViewBindingBottomSheetDialogFragment] that displays basic music information and a series of
@ -102,7 +102,7 @@ abstract class MenuDialogFragment<M : Menu> :
private fun updateMenu(menu: Menu?) { private fun updateMenu(menu: Menu?) {
if (menu == null) { if (menu == null) {
T.d("No menu to show, navigating away") L.d("No menu to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.StateFlow
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.playback.PlaySong import org.oxycblt.auxio.playback.PlaySong
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Manages the state information for [MenuDialogFragment] implementations. * Manages the state information for [MenuDialogFragment] implementations.
@ -55,7 +55,7 @@ class MenuViewModel @Inject constructor(private val musicRepository: MusicReposi
fun setMenu(parcel: Menu.Parcel) { fun setMenu(parcel: Menu.Parcel) {
_currentMenu.value = unpackParcel(parcel) _currentMenu.value = unpackParcel(parcel)
if (_currentMenu.value == null) { if (_currentMenu.value == null) {
T.w("Given menu parcel $parcel was invalid") L.w("Given menu parcel $parcel was invalid")
} }
} }

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.recycler.MaterialDragCallback.ViewHolder import org.oxycblt.auxio.list.recycler.MaterialDragCallback.ViewHolder
import org.oxycblt.auxio.util.getDimen import org.oxycblt.auxio.util.getDimen
import org.oxycblt.auxio.util.getInteger import org.oxycblt.auxio.util.getInteger
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A highly customized [ItemTouchHelper.Callback] that enables some extra eye candy in editable UIs, * A highly customized [ItemTouchHelper.Callback] that enables some extra eye candy in editable UIs,
@ -94,7 +94,7 @@ abstract class MaterialDragCallback : ItemTouchHelper.Callback() {
// this is only done once when the item is initially picked up. // this is only done once when the item is initially picked up.
// TODO: I think this is possible to improve with a raw ValueAnimator. // TODO: I think this is possible to improve with a raw ValueAnimator.
if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) { if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
T.d("Lifting ViewHolder") L.d("Lifting ViewHolder")
val bg = holder.background val bg = holder.background
val elevation = recyclerView.context.getDimen(MR.dimen.m3_sys_elevation_level4) val elevation = recyclerView.context.getDimen(MR.dimen.m3_sys_elevation_level4)
@ -136,7 +136,7 @@ abstract class MaterialDragCallback : ItemTouchHelper.Callback() {
// This function can be called multiple times, so only start the animation when the view's // This function can be called multiple times, so only start the animation when the view's
// translationZ is already non-zero. // translationZ is already non-zero.
if (holder.root.translationZ != 0f) { if (holder.root.translationZ != 0f) {
T.d("Lifting ViewHolder") L.d("Lifting ViewHolder")
val bg = holder.background val bg = holder.background
val elevation = recyclerView.context.getDimen(MR.dimen.m3_sys_elevation_level4) val elevation = recyclerView.context.getDimen(MR.dimen.m3_sys_elevation_level4)

View file

@ -22,6 +22,9 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Parcelable import android.os.Parcelable
import androidx.room.TypeConverter import androidx.room.TypeConverter
import java.security.MessageDigest
import java.util.UUID
import kotlin.math.max
import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.oxycblt.auxio.image.extractor.Cover import org.oxycblt.auxio.image.extractor.Cover
@ -36,9 +39,6 @@ import org.oxycblt.auxio.music.info.ReleaseType
import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
import org.oxycblt.auxio.util.concatLocalized import org.oxycblt.auxio.util.concatLocalized
import org.oxycblt.auxio.util.toUuidOrNull import org.oxycblt.auxio.util.toUuidOrNull
import java.security.MessageDigest
import java.util.UUID
import kotlin.math.max
/** /**
* Abstract music data. This contains universal information about all concrete music * Abstract music data. This contains universal information about all concrete music

View file

@ -44,7 +44,7 @@ import org.oxycblt.auxio.music.user.MutableUserLibrary
import org.oxycblt.auxio.music.user.UserLibrary import org.oxycblt.auxio.music.user.UserLibrary
import org.oxycblt.auxio.util.DEFAULT_TIMEOUT import org.oxycblt.auxio.util.DEFAULT_TIMEOUT
import org.oxycblt.auxio.util.forEachWithTimeout import org.oxycblt.auxio.util.forEachWithTimeout
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Primary manager of music information and loading. * Primary manager of music information and loading.
@ -242,51 +242,51 @@ constructor(
@Synchronized @Synchronized
override fun addUpdateListener(listener: MusicRepository.UpdateListener) { override fun addUpdateListener(listener: MusicRepository.UpdateListener) {
T.d("Adding $listener to update listeners") L.d("Adding $listener to update listeners")
updateListeners.add(listener) updateListeners.add(listener)
listener.onMusicChanges(MusicRepository.Changes(deviceLibrary = true, userLibrary = true)) listener.onMusicChanges(MusicRepository.Changes(deviceLibrary = true, userLibrary = true))
} }
@Synchronized @Synchronized
override fun removeUpdateListener(listener: MusicRepository.UpdateListener) { override fun removeUpdateListener(listener: MusicRepository.UpdateListener) {
T.d("Removing $listener to update listeners") L.d("Removing $listener to update listeners")
if (!updateListeners.remove(listener)) { if (!updateListeners.remove(listener)) {
T.w("Update listener $listener was not added prior, cannot remove") L.w("Update listener $listener was not added prior, cannot remove")
} }
} }
@Synchronized @Synchronized
override fun addIndexingListener(listener: MusicRepository.IndexingListener) { override fun addIndexingListener(listener: MusicRepository.IndexingListener) {
T.d("Adding $listener to indexing listeners") L.d("Adding $listener to indexing listeners")
indexingListeners.add(listener) indexingListeners.add(listener)
listener.onIndexingStateChanged() listener.onIndexingStateChanged()
} }
@Synchronized @Synchronized
override fun removeIndexingListener(listener: MusicRepository.IndexingListener) { override fun removeIndexingListener(listener: MusicRepository.IndexingListener) {
T.d("Removing $listener from indexing listeners") L.d("Removing $listener from indexing listeners")
if (!indexingListeners.remove(listener)) { if (!indexingListeners.remove(listener)) {
T.w("Indexing listener $listener was not added prior, cannot remove") L.w("Indexing listener $listener was not added prior, cannot remove")
} }
} }
@Synchronized @Synchronized
override fun registerWorker(worker: MusicRepository.IndexingWorker) { override fun registerWorker(worker: MusicRepository.IndexingWorker) {
if (indexingWorker != null) { if (indexingWorker != null) {
T.w("Worker is already registered") L.w("Worker is already registered")
return return
} }
T.d("Registering worker $worker") L.d("Registering worker $worker")
indexingWorker = worker indexingWorker = worker
} }
@Synchronized @Synchronized
override fun unregisterWorker(worker: MusicRepository.IndexingWorker) { override fun unregisterWorker(worker: MusicRepository.IndexingWorker) {
if (indexingWorker !== worker) { if (indexingWorker !== worker) {
T.w("Given worker did not match current worker") L.w("Given worker did not match current worker")
return return
} }
T.d("Unregistering worker $worker") L.d("Unregistering worker $worker")
indexingWorker = null indexingWorker = null
currentIndexingState = null currentIndexingState = null
} }
@ -298,42 +298,42 @@ constructor(
override suspend fun createPlaylist(name: String, songs: List<Song>) { override suspend fun createPlaylist(name: String, songs: List<Song>) {
val userLibrary = synchronized(this) { userLibrary ?: return } val userLibrary = synchronized(this) { userLibrary ?: return }
T.d("Creating playlist $name with ${songs.size} songs") L.d("Creating playlist $name with ${songs.size} songs")
userLibrary.createPlaylist(name, songs) userLibrary.createPlaylist(name, songs)
withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) }
} }
override suspend fun renamePlaylist(playlist: Playlist, name: String) { override suspend fun renamePlaylist(playlist: Playlist, name: String) {
val userLibrary = synchronized(this) { userLibrary ?: return } val userLibrary = synchronized(this) { userLibrary ?: return }
T.d("Renaming $playlist to $name") L.d("Renaming $playlist to $name")
userLibrary.renamePlaylist(playlist, name) userLibrary.renamePlaylist(playlist, name)
withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) }
} }
override suspend fun deletePlaylist(playlist: Playlist) { override suspend fun deletePlaylist(playlist: Playlist) {
val userLibrary = synchronized(this) { userLibrary ?: return } val userLibrary = synchronized(this) { userLibrary ?: return }
T.d("Deleting $playlist") L.d("Deleting $playlist")
userLibrary.deletePlaylist(playlist) userLibrary.deletePlaylist(playlist)
withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) }
} }
override suspend fun addToPlaylist(songs: List<Song>, playlist: Playlist) { override suspend fun addToPlaylist(songs: List<Song>, playlist: Playlist) {
val userLibrary = synchronized(this) { userLibrary ?: return } val userLibrary = synchronized(this) { userLibrary ?: return }
T.d("Adding ${songs.size} songs to $playlist") L.d("Adding ${songs.size} songs to $playlist")
userLibrary.addToPlaylist(playlist, songs) userLibrary.addToPlaylist(playlist, songs)
withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) }
} }
override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>) { override suspend fun rewritePlaylist(playlist: Playlist, songs: List<Song>) {
val userLibrary = synchronized(this) { userLibrary ?: return } val userLibrary = synchronized(this) { userLibrary ?: return }
T.d("Rewriting $playlist with ${songs.size} songs") L.d("Rewriting $playlist with ${songs.size} songs")
userLibrary.rewritePlaylist(playlist, songs) userLibrary.rewritePlaylist(playlist, songs)
withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) }
} }
@Synchronized @Synchronized
override fun requestIndex(withCache: Boolean) { override fun requestIndex(withCache: Boolean) {
T.d("Requesting index operation [cache=$withCache]") L.d("Requesting index operation [cache=$withCache]")
indexingWorker?.requestIndex(withCache) indexingWorker?.requestIndex(withCache)
} }
@ -345,13 +345,13 @@ constructor(
indexImpl(context, scope, withCache) indexImpl(context, scope, withCache)
} catch (e: CancellationException) { } catch (e: CancellationException) {
// Got cancelled, propagate upwards to top-level co-routine. // Got cancelled, propagate upwards to top-level co-routine.
T.d("Loading routine was cancelled") L.d("Loading routine was cancelled")
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
// Music loading process failed due to something we have not handled. // Music loading process failed due to something we have not handled.
// TODO: Still want to display this error eventually // TODO: Still want to display this error eventually
T.e("Music indexing failed") L.e("Music indexing failed")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
emitIndexingCompletion(e) emitIndexingCompletion(e)
} }
} }
@ -364,7 +364,7 @@ constructor(
// done at the UI level, but that intertwines logic and display too much. // done at the UI level, but that intertwines logic and display too much.
if (ContextCompat.checkSelfPermission(context, PERMISSION_READ_AUDIO) == if (ContextCompat.checkSelfPermission(context, PERMISSION_READ_AUDIO) ==
PackageManager.PERMISSION_DENIED) { PackageManager.PERMISSION_DENIED) {
T.e("Permissions were not granted") L.e("Permissions were not granted")
throw NoAudioPermissionException() throw NoAudioPermissionException()
} }
@ -383,7 +383,7 @@ constructor(
// to figure out what songs are (probably) on the device, and the latter will be needed // to figure out what songs are (probably) on the device, and the latter will be needed
// for discovery (described later). These have no shared state, so they are done in // for discovery (described later). These have no shared state, so they are done in
// parallel. // parallel.
T.d("Starting MediaStore query") L.d("Starting MediaStore query")
emitIndexingProgress(IndexingProgress.Indeterminate) emitIndexingProgress(IndexingProgress.Indeterminate)
val mediaStoreQueryJob = val mediaStoreQueryJob =
@ -404,18 +404,18 @@ constructor(
// identical to calling async. // identical to calling async.
val cache = val cache =
if (withCache) { if (withCache) {
T.d("Reading cache") L.d("Reading cache")
cacheRepository.readCache() cacheRepository.readCache()
} else { } else {
null null
} }
T.d("Awaiting MediaStore query") L.d("Awaiting MediaStore query")
val query = mediaStoreQueryJob.await().getOrThrow() val query = mediaStoreQueryJob.await().getOrThrow()
// We now have all the information required to start the "discovery" process. This // We now have all the information required to start the "discovery" process. This
// is the point at which Auxio starts scanning each file given from MediaStore and // is the point at which Auxio starts scanning each file given from MediaStore and
// transforming it into a music library. MediaStore normally // transforming it into a music library. MediaStore normally
T.d("Starting discovery") L.d("Starting discovery")
val incompleteSongs = Channel<RawSong>(Channel.UNLIMITED) // Not fully populated w/metadata val incompleteSongs = Channel<RawSong>(Channel.UNLIMITED) // Not fully populated w/metadata
val completeSongs = Channel<RawSong>(Channel.UNLIMITED) // Populated with quality metadata val completeSongs = Channel<RawSong>(Channel.UNLIMITED) // Populated with quality metadata
val processedSongs = Channel<RawSong>(Channel.UNLIMITED) // Transformed into SongImpl val processedSongs = Channel<RawSong>(Channel.UNLIMITED) // Transformed into SongImpl
@ -423,7 +423,7 @@ constructor(
// MediaStoreExtractor discovers all music on the device, and forwards them to either // MediaStoreExtractor discovers all music on the device, and forwards them to either
// DeviceLibrary if cached metadata exists for it, or TagExtractor if cached metadata // DeviceLibrary if cached metadata exists for it, or TagExtractor if cached metadata
// does not exist. In the latter situation, it also applies it's own (inferior) metadata. // does not exist. In the latter situation, it also applies it's own (inferior) metadata.
T.d("Starting MediaStore discovery") L.d("Starting MediaStore discovery")
val mediaStoreJob = val mediaStoreJob =
scope.async { scope.async {
try { try {
@ -432,7 +432,7 @@ constructor(
// To prevent a deadlock, we want to close the channel with an exception // To prevent a deadlock, we want to close the channel with an exception
// to cascade to and cancel all other routines before finally bubbling up // to cascade to and cancel all other routines before finally bubbling up
// to the main extractor loop. // to the main extractor loop.
T.e("MediaStore extraction failed: $e") L.e("MediaStore extraction failed: $e")
incompleteSongs.close( incompleteSongs.close(
Exception("MediaStore extraction failed: ${e.stackTraceToString()}")) Exception("MediaStore extraction failed: ${e.stackTraceToString()}"))
return@async return@async
@ -442,13 +442,13 @@ constructor(
// TagExtractor takes the incomplete songs from MediaStoreExtractor, parses up-to-date // TagExtractor takes the incomplete songs from MediaStoreExtractor, parses up-to-date
// metadata for them, and then forwards it to DeviceLibrary. // metadata for them, and then forwards it to DeviceLibrary.
T.d("Starting tag extraction") L.d("Starting tag extraction")
val tagJob = val tagJob =
scope.async { scope.async {
try { try {
tagExtractor.consume(incompleteSongs, completeSongs) tagExtractor.consume(incompleteSongs, completeSongs)
} catch (e: Exception) { } catch (e: Exception) {
T.e("Tag extraction failed: $e") L.e("Tag extraction failed: $e")
completeSongs.close( completeSongs.close(
Exception("Tag extraction failed: ${e.stackTraceToString()}")) Exception("Tag extraction failed: ${e.stackTraceToString()}"))
return@async return@async
@ -458,7 +458,7 @@ constructor(
// DeviceLibrary constructs music parent instances as song information is provided, // DeviceLibrary constructs music parent instances as song information is provided,
// and then forwards them to the primary loading loop. // and then forwards them to the primary loading loop.
T.d("Starting DeviceLibrary creation") L.d("Starting DeviceLibrary creation")
val deviceLibraryJob = val deviceLibraryJob =
scope.async(Dispatchers.Default) { scope.async(Dispatchers.Default) {
val deviceLibrary = val deviceLibrary =
@ -466,7 +466,7 @@ constructor(
deviceLibraryFactory.create( deviceLibraryFactory.create(
completeSongs, processedSongs, separators, nameFactory) completeSongs, processedSongs, separators, nameFactory)
} catch (e: Exception) { } catch (e: Exception) {
T.e("DeviceLibrary creation failed: $e") L.e("DeviceLibrary creation failed: $e")
processedSongs.close( processedSongs.close(
Exception("DeviceLibrary creation failed: ${e.stackTraceToString()}")) Exception("DeviceLibrary creation failed: ${e.stackTraceToString()}"))
return@async Result.failure(e) return@async Result.failure(e)
@ -497,14 +497,14 @@ constructor(
// that the short-circuit occurs so quickly as to break the UI. // that the short-circuit occurs so quickly as to break the UI.
// TODO: Do not error, instead just wipe the entire library. // TODO: Do not error, instead just wipe the entire library.
if (rawSongs.isEmpty()) { if (rawSongs.isEmpty()) {
T.e("Music library was empty") L.e("Music library was empty")
throw NoMusicException() throw NoMusicException()
} }
// Now that the library is effectively loaded, we can start the finalization step, which // Now that the library is effectively loaded, we can start the finalization step, which
// involves writing new cache information and creating more music data that is derived // involves writing new cache information and creating more music data that is derived
// from the library (e.g playlists) // from the library (e.g playlists)
T.d("Discovered ${rawSongs.size} songs, starting finalization") L.d("Discovered ${rawSongs.size} songs, starting finalization")
// We have no idea how long the cache will take, and the playlist construction // We have no idea how long the cache will take, and the playlist construction
// will be too fast to indicate, so switch back to an indeterminate state. // will be too fast to indicate, so switch back to an indeterminate state.
@ -513,7 +513,7 @@ constructor(
// The UserLibrary job is split into a query and construction step, a la MediaStore. // The UserLibrary job is split into a query and construction step, a la MediaStore.
// This way, we can start working on playlists even as DeviceLibrary might still be // This way, we can start working on playlists even as DeviceLibrary might still be
// working on parent information. // working on parent information.
T.d("Starting UserLibrary query") L.d("Starting UserLibrary query")
val userLibraryQueryJob = val userLibraryQueryJob =
scope.async { scope.async {
val rawPlaylists = val rawPlaylists =
@ -530,20 +530,20 @@ constructor(
// since the playlist read will probably take some time. // since the playlist read will probably take some time.
// TODO: Read/write from the cache incrementally instead of in bulk? // TODO: Read/write from the cache incrementally instead of in bulk?
if (cache == null || cache.invalidated) { if (cache == null || cache.invalidated) {
T.d("Writing cache [why=${cache?.invalidated}]") L.d("Writing cache [why=${cache?.invalidated}]")
cacheRepository.writeCache(rawSongs) cacheRepository.writeCache(rawSongs)
} }
// Create UserLibrary once we finally get the required components for it. // Create UserLibrary once we finally get the required components for it.
T.d("Awaiting UserLibrary query") L.d("Awaiting UserLibrary query")
val rawPlaylists = userLibraryQueryJob.await().getOrThrow() val rawPlaylists = userLibraryQueryJob.await().getOrThrow()
T.d("Awaiting DeviceLibrary creation") L.d("Awaiting DeviceLibrary creation")
val deviceLibrary = deviceLibraryJob.await().getOrThrow() val deviceLibrary = deviceLibraryJob.await().getOrThrow()
T.d("Starting UserLibrary creation") L.d("Starting UserLibrary creation")
val userLibrary = userLibraryFactory.create(rawPlaylists, deviceLibrary, nameFactory) val userLibrary = userLibraryFactory.create(rawPlaylists, deviceLibrary, nameFactory)
// Loading process is functionally done, indicate such // Loading process is functionally done, indicate such
T.d( L.d(
"Successfully indexed music library [device=$deviceLibrary " + "Successfully indexed music library [device=$deviceLibrary " +
"user=$userLibrary time=${System.currentTimeMillis() - start}]") "user=$userLibrary time=${System.currentTimeMillis() - start}]")
emitIndexingCompletion(null) emitIndexingCompletion(null)
@ -559,7 +559,7 @@ constructor(
deviceLibraryChanged = this.deviceLibrary != deviceLibrary deviceLibraryChanged = this.deviceLibrary != deviceLibrary
userLibraryChanged = this.userLibrary != userLibrary userLibraryChanged = this.userLibrary != userLibrary
if (!deviceLibraryChanged && !userLibraryChanged) { if (!deviceLibraryChanged && !userLibraryChanged) {
T.d("Library has not changed, skipping update") L.d("Library has not changed, skipping update")
return return
} }
@ -589,7 +589,7 @@ constructor(
synchronized(this) { synchronized(this) {
previousCompletedState = IndexingState.Completed(error) previousCompletedState = IndexingState.Completed(error)
currentIndexingState = null currentIndexingState = null
T.d("Dispatching completion state [error=$error]") L.d("Dispatching completion state [error=$error]")
for (listener in indexingListeners) { for (listener in indexingListeners) {
listener.onIndexingStateChanged() listener.onIndexingStateChanged()
} }
@ -599,7 +599,7 @@ constructor(
@Synchronized @Synchronized
private fun dispatchLibraryChange(device: Boolean, user: Boolean) { private fun dispatchLibraryChange(device: Boolean, user: Boolean) {
val changes = MusicRepository.Changes(device, user) val changes = MusicRepository.Changes(device, user)
T.d("Dispatching library change [changes=$changes]") L.d("Dispatching library change [changes=$changes]")
for (listener in updateListeners) { for (listener in updateListeners) {
listener.onMusicChanges(changes) listener.onMusicChanges(changes)
} }

View file

@ -26,7 +26,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.dirs.MusicDirectories import org.oxycblt.auxio.music.dirs.MusicDirectories
import org.oxycblt.auxio.music.fs.DocumentPathFactory import org.oxycblt.auxio.music.fs.DocumentPathFactory
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* User configuration specific to music system. * User configuration specific to music system.
@ -108,11 +108,11 @@ constructor(
getString(R.string.set_key_music_dirs_include), getString(R.string.set_key_music_dirs_include),
getString(R.string.set_key_separators), getString(R.string.set_key_separators),
getString(R.string.set_key_auto_sort_names) -> { getString(R.string.set_key_auto_sort_names) -> {
T.d("Dispatching indexing setting change for $key") L.d("Dispatching indexing setting change for $key")
listener.onIndexingSettingChanged() listener.onIndexingSettingChanged()
} }
getString(R.string.set_key_observing) -> { getString(R.string.set_key_observing) -> {
T.d("Dispatching observing setting change") L.d("Dispatching observing setting change")
listener.onObservingChanged() listener.onObservingChanged()
} }
} }

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.music.external.ExportConfig
import org.oxycblt.auxio.music.external.ExternalPlaylistManager import org.oxycblt.auxio.music.external.ExternalPlaylistManager
import org.oxycblt.auxio.util.Event import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent import org.oxycblt.auxio.util.MutableEvent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewModel] providing data specific to the music loading process. * A [ViewModel] providing data specific to the music loading process.
@ -93,7 +93,7 @@ constructor(
deviceLibrary.artists.size, deviceLibrary.artists.size,
deviceLibrary.genres.size, deviceLibrary.genres.size,
deviceLibrary.songs.sumOf { it.durationMs }) deviceLibrary.songs.sumOf { it.durationMs })
T.d("Updated statistics: ${_statistics.value}") L.d("Updated statistics: ${_statistics.value}")
} }
override fun onIndexingStateChanged() { override fun onIndexingStateChanged() {
@ -102,13 +102,13 @@ constructor(
/** Requests that the music library should be re-loaded while leveraging the cache. */ /** Requests that the music library should be re-loaded while leveraging the cache. */
fun refresh() { fun refresh() {
T.d("Refreshing library") L.d("Refreshing library")
musicRepository.requestIndex(true) musicRepository.requestIndex(true)
} }
/** Requests that the music library be re-loaded without the cache. */ /** Requests that the music library be re-loaded without the cache. */
fun rescan() { fun rescan() {
T.d("Rescanning library") L.d("Rescanning library")
musicRepository.requestIndex(false) musicRepository.requestIndex(false)
} }
@ -126,7 +126,7 @@ constructor(
reason: PlaylistDecision.New.Reason = PlaylistDecision.New.Reason.NEW reason: PlaylistDecision.New.Reason = PlaylistDecision.New.Reason.NEW
) { ) {
if (name != null) { if (name != null) {
T.d("Creating $name with ${songs.size} songs]") L.d("Creating $name with ${songs.size} songs]")
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
musicRepository.createPlaylist(name, songs) musicRepository.createPlaylist(name, songs)
val message = val message =
@ -138,7 +138,7 @@ constructor(
_playlistMessage.put(message) _playlistMessage.put(message)
} }
} else { } else {
T.d("Launching creation dialog for ${songs.size} songs") L.d("Launching creation dialog for ${songs.size} songs")
_playlistDecision.put(PlaylistDecision.New(songs, null, reason)) _playlistDecision.put(PlaylistDecision.New(songs, null, reason))
} }
} }
@ -157,7 +157,7 @@ constructor(
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val importedPlaylist = externalPlaylistManager.import(uri) val importedPlaylist = externalPlaylistManager.import(uri)
if (importedPlaylist == null) { if (importedPlaylist == null) {
T.e("Could not import playlist") L.e("Could not import playlist")
_playlistMessage.put(PlaylistMessage.ImportFailed) _playlistMessage.put(PlaylistMessage.ImportFailed)
return@launch return@launch
} }
@ -169,7 +169,7 @@ constructor(
} }
if (songs.isEmpty()) { if (songs.isEmpty()) {
T.e("No songs found") L.e("No songs found")
_playlistMessage.put(PlaylistMessage.ImportFailed) _playlistMessage.put(PlaylistMessage.ImportFailed)
return@launch return@launch
} }
@ -193,7 +193,7 @@ constructor(
} }
} }
} else { } else {
T.d("Launching import picker") L.d("Launching import picker")
_playlistDecision.put(PlaylistDecision.Import(target)) _playlistDecision.put(PlaylistDecision.Import(target))
} }
} }
@ -206,7 +206,7 @@ constructor(
*/ */
fun exportPlaylist(playlist: Playlist, uri: Uri? = null, config: ExportConfig? = null) { fun exportPlaylist(playlist: Playlist, uri: Uri? = null, config: ExportConfig? = null) {
if (uri != null && config != null) { if (uri != null && config != null) {
T.d("Exporting playlist to $uri") L.d("Exporting playlist to $uri")
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
if (externalPlaylistManager.export(playlist, uri, config)) { if (externalPlaylistManager.export(playlist, uri, config)) {
_playlistMessage.put(PlaylistMessage.ExportSuccess) _playlistMessage.put(PlaylistMessage.ExportSuccess)
@ -215,7 +215,7 @@ constructor(
} }
} }
} else { } else {
T.d("Launching export dialog") L.d("Launching export dialog")
_playlistDecision.put(PlaylistDecision.Export(playlist)) _playlistDecision.put(PlaylistDecision.Export(playlist))
} }
} }
@ -237,7 +237,7 @@ constructor(
reason: PlaylistDecision.Rename.Reason = PlaylistDecision.Rename.Reason.ACTION reason: PlaylistDecision.Rename.Reason = PlaylistDecision.Rename.Reason.ACTION
) { ) {
if (name != null) { if (name != null) {
T.d("Renaming $playlist to $name") L.d("Renaming $playlist to $name")
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
musicRepository.renamePlaylist(playlist, name) musicRepository.renamePlaylist(playlist, name)
if (applySongs.isNotEmpty()) { if (applySongs.isNotEmpty()) {
@ -251,7 +251,7 @@ constructor(
_playlistMessage.put(message) _playlistMessage.put(message)
} }
} else { } else {
T.d("Launching rename dialog for $playlist") L.d("Launching rename dialog for $playlist")
_playlistDecision.put(PlaylistDecision.Rename(playlist, null, applySongs, reason)) _playlistDecision.put(PlaylistDecision.Rename(playlist, null, applySongs, reason))
} }
} }
@ -266,13 +266,13 @@ constructor(
*/ */
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) { fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
if (rude) { if (rude) {
T.d("Deleting $playlist") L.d("Deleting $playlist")
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
musicRepository.deletePlaylist(playlist) musicRepository.deletePlaylist(playlist)
_playlistMessage.put(PlaylistMessage.DeleteSuccess) _playlistMessage.put(PlaylistMessage.DeleteSuccess)
} }
} else { } else {
T.d("Launching deletion dialog for $playlist") L.d("Launching deletion dialog for $playlist")
_playlistDecision.put(PlaylistDecision.Delete(playlist)) _playlistDecision.put(PlaylistDecision.Delete(playlist))
} }
} }
@ -284,7 +284,7 @@ constructor(
* @param playlist The [Playlist] to add to. If null, the user will be prompted for one. * @param playlist The [Playlist] to add to. If null, the user will be prompted for one.
*/ */
fun addToPlaylist(song: Song, playlist: Playlist? = null) { fun addToPlaylist(song: Song, playlist: Playlist? = null) {
T.d("Adding $song to playlist") L.d("Adding $song to playlist")
addToPlaylist(listOf(song), playlist) addToPlaylist(listOf(song), playlist)
} }
@ -295,7 +295,7 @@ constructor(
* @param playlist The [Playlist] to add to. If null, the user will be prompted for one. * @param playlist The [Playlist] to add to. If null, the user will be prompted for one.
*/ */
fun addToPlaylist(album: Album, playlist: Playlist? = null) { fun addToPlaylist(album: Album, playlist: Playlist? = null) {
T.d("Adding $album to playlist") L.d("Adding $album to playlist")
addToPlaylist(listSettings.albumSongSort.songs(album.songs), playlist) addToPlaylist(listSettings.albumSongSort.songs(album.songs), playlist)
} }
@ -306,7 +306,7 @@ constructor(
* @param playlist The [Playlist] to add to. If null, the user will be prompted for one. * @param playlist The [Playlist] to add to. If null, the user will be prompted for one.
*/ */
fun addToPlaylist(artist: Artist, playlist: Playlist? = null) { fun addToPlaylist(artist: Artist, playlist: Playlist? = null) {
T.d("Adding $artist to playlist") L.d("Adding $artist to playlist")
addToPlaylist(listSettings.artistSongSort.songs(artist.songs), playlist) addToPlaylist(listSettings.artistSongSort.songs(artist.songs), playlist)
} }
@ -317,7 +317,7 @@ constructor(
* @param playlist The [Playlist] to add to. If null, the user will be prompted for one. * @param playlist The [Playlist] to add to. If null, the user will be prompted for one.
*/ */
fun addToPlaylist(genre: Genre, playlist: Playlist? = null) { fun addToPlaylist(genre: Genre, playlist: Playlist? = null) {
T.d("Adding $genre to playlist") L.d("Adding $genre to playlist")
addToPlaylist(listSettings.genreSongSort.songs(genre.songs), playlist) addToPlaylist(listSettings.genreSongSort.songs(genre.songs), playlist)
} }
@ -329,13 +329,13 @@ constructor(
*/ */
fun addToPlaylist(songs: List<Song>, playlist: Playlist? = null) { fun addToPlaylist(songs: List<Song>, playlist: Playlist? = null) {
if (playlist != null) { if (playlist != null) {
T.d("Adding ${songs.size} songs to $playlist") L.d("Adding ${songs.size} songs to $playlist")
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
musicRepository.addToPlaylist(songs, playlist) musicRepository.addToPlaylist(songs, playlist)
_playlistMessage.put(PlaylistMessage.AddSuccess) _playlistMessage.put(PlaylistMessage.AddSuccess)
} }
} else { } else {
T.d("Launching addition dialog for songs=${songs.size}") L.d("Launching addition dialog for songs=${songs.size}")
_playlistDecision.put(PlaylistDecision.Add(songs)) _playlistDecision.put(PlaylistDecision.Add(songs))
} }
} }

View file

@ -20,7 +20,7 @@ package org.oxycblt.auxio.music.cache
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.music.device.RawSong import org.oxycblt.auxio.music.device.RawSong
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A repository allowing access to cached metadata obtained in prior music loading operations. * A repository allowing access to cached metadata obtained in prior music loading operations.
@ -50,11 +50,11 @@ class CacheRepositoryImpl @Inject constructor(private val cachedSongsDao: Cached
// Faster to load the whole database into memory than do a query on each // Faster to load the whole database into memory than do a query on each
// populate call. // populate call.
val songs = cachedSongsDao.readSongs() val songs = cachedSongsDao.readSongs()
T.d("Successfully read ${songs.size} songs from cache") L.d("Successfully read ${songs.size} songs from cache")
CacheImpl(songs) CacheImpl(songs)
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to load cache database.") L.e("Unable to load cache database.")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
null null
} }
@ -62,12 +62,12 @@ class CacheRepositoryImpl @Inject constructor(private val cachedSongsDao: Cached
try { try {
// Still write out whatever data was extracted. // Still write out whatever data was extracted.
cachedSongsDao.nukeSongs() cachedSongsDao.nukeSongs()
T.d("Successfully deleted old cache") L.d("Successfully deleted old cache")
cachedSongsDao.insertSongs(rawSongs.map(CachedSong::fromRaw)) cachedSongsDao.insertSongs(rawSongs.map(CachedSong::fromRaw))
T.d("Successfully wrote ${rawSongs.size} songs to cache") L.d("Successfully wrote ${rawSongs.size} songs to cache")
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to save cache database.") L.e("Unable to save cache database.")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
} }
} }
} }

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A dialog that allows the user to pick a specific playlist to add song(s) to. * A dialog that allows the user to pick a specific playlist to add song(s) to.
@ -105,7 +105,7 @@ class AddToPlaylistDialog :
private fun updatePendingSongs(songs: List<Song>?) { private fun updatePendingSongs(songs: List<Song>?) {
if (songs == null) { if (songs == null) {
T.d("No songs to show choices for, navigating away") L.d("No songs to show choices for, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
} }
} }

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingMaterialDialogFragment] that asks the user to confirm the deletion of a [Playlist]. * A [ViewBindingMaterialDialogFragment] that asks the user to confirm the deletion of a [Playlist].
@ -76,7 +76,7 @@ class DeletePlaylistDialog : ViewBindingMaterialDialogFragment<DialogDeletePlayl
private fun updatePlaylistToDelete(playlist: Playlist?) { private fun updatePlaylistToDelete(playlist: Playlist?) {
if (playlist == null) { if (playlist == null) {
T.d("No playlist to delete, navigating away") L.d("No playlist to delete, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.music.external.M3U
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A dialog that allows the user to configure how a playlist will be exported to a file. * A dialog that allows the user to configure how a playlist will be exported to a file.
@ -72,18 +72,18 @@ class ExportPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistExp
registerForActivityResult(ActivityResultContracts.CreateDocument(M3U.MIME_TYPE)) { uri registerForActivityResult(ActivityResultContracts.CreateDocument(M3U.MIME_TYPE)) { uri
-> ->
if (uri == null) { if (uri == null) {
T.w("No URI returned from file picker") L.w("No URI returned from file picker")
return@registerForActivityResult return@registerForActivityResult
} }
val playlist = pickerModel.currentPlaylistToExport.value val playlist = pickerModel.currentPlaylistToExport.value
if (playlist == null) { if (playlist == null) {
T.w("No playlist to export") L.w("No playlist to export")
findNavController().navigateUp() findNavController().navigateUp()
return@registerForActivityResult return@registerForActivityResult
} }
T.d("Received playlist URI $uri") L.d("Received playlist URI $uri")
musicModel.exportPlaylist(playlist, uri, pickerModel.currentExportConfig.value) musicModel.exportPlaylist(playlist, uri, pickerModel.currentExportConfig.value)
findNavController().navigateUp() findNavController().navigateUp()
} }
@ -128,7 +128,7 @@ class ExportPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistExp
private fun updatePlaylistToExport(playlist: Playlist?) { private fun updatePlaylistToExport(playlist: Playlist?) {
if (playlist == null) { if (playlist == null) {
T.d("No playlist to export, leaving") L.d("No playlist to export, leaving")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -35,7 +35,7 @@ import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A dialog allowing the name of a new playlist to be chosen before committing it to the database. * A dialog allowing the name of a new playlist to be chosen before committing it to the database.
@ -98,7 +98,7 @@ class NewPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNameBi
private fun updatePendingPlaylist(pendingNewPlaylist: PendingNewPlaylist?) { private fun updatePendingPlaylist(pendingNewPlaylist: PendingNewPlaylist?) {
if (pendingNewPlaylist == null) { if (pendingNewPlaylist == null) {
T.d("No playlist to create, leaving") L.d("No playlist to create, leaving")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.external.ExportConfig import org.oxycblt.auxio.music.external.ExportConfig
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewModel] managing the state of the playlist picker dialogs. * A [ViewModel] managing the state of the playlist picker dialogs.
@ -99,7 +99,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
pendingPlaylist.template, pendingPlaylist.template,
pendingPlaylist.reason) pendingPlaylist.reason)
} }
T.d("Updated pending playlist: ${_currentPendingNewPlaylist.value?.preferredName}") L.d("Updated pending playlist: ${_currentPendingNewPlaylist.value?.preferredName}")
_currentSongsToAdd.value = _currentSongsToAdd.value =
_currentSongsToAdd.value?.let { pendingSongs -> _currentSongsToAdd.value?.let { pendingSongs ->
@ -108,7 +108,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
.ifEmpty { null } .ifEmpty { null }
.also { refreshChoicesWith = it } .also { refreshChoicesWith = it }
} }
T.d("Updated songs to add: ${_currentSongsToAdd.value?.size} songs") L.d("Updated songs to add: ${_currentSongsToAdd.value?.size} songs")
} }
val chosenName = _chosenName.value val chosenName = _chosenName.value
@ -120,7 +120,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
// Nothing to do. // Nothing to do.
} }
} }
T.d("Updated chosen name to $chosenName") L.d("Updated chosen name to $chosenName")
refreshChoicesWith = refreshChoicesWith ?: _currentSongsToAdd.value refreshChoicesWith = refreshChoicesWith ?: _currentSongsToAdd.value
// TODO: Add music syncing for other playlist states here // TODO: Add music syncing for other playlist states here
@ -129,7 +129,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
_currentPlaylistToExport.value?.let { playlist -> _currentPlaylistToExport.value?.let { playlist ->
musicRepository.userLibrary?.findPlaylist(playlist.uid) musicRepository.userLibrary?.findPlaylist(playlist.uid)
} }
T.d("Updated playlist to export to ${_currentPlaylistToExport.value}") L.d("Updated playlist to export to ${_currentPlaylistToExport.value}")
} }
refreshChoicesWith?.let(::refreshPlaylistChoices) refreshChoicesWith?.let(::refreshPlaylistChoices)
@ -152,7 +152,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
template: String?, template: String?,
reason: PlaylistDecision.New.Reason reason: PlaylistDecision.New.Reason
) { ) {
T.d("Opening ${songUids.size} songs to create a playlist from") L.d("Opening ${songUids.size} songs to create a playlist from")
val userLibrary = musicRepository.userLibrary ?: return val userLibrary = musicRepository.userLibrary ?: return
val songs = val songs =
musicRepository.deviceLibrary musicRepository.deviceLibrary
@ -166,10 +166,10 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
var possibleName: String var possibleName: String
do { do {
possibleName = context.getString(R.string.fmt_def_playlist, i) possibleName = context.getString(R.string.fmt_def_playlist, i)
T.d("Trying $possibleName as a playlist name") L.d("Trying $possibleName as a playlist name")
++i ++i
} while (userLibrary.playlists.any { it.name.resolve(context) == possibleName }) } while (userLibrary.playlists.any { it.name.resolve(context) == possibleName })
T.d("$possibleName is unique, using it as the playlist name") L.d("$possibleName is unique, using it as the playlist name")
possibleName possibleName
} }
@ -177,7 +177,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
if (possibleName != null && songs != null) { if (possibleName != null && songs != null) {
PendingNewPlaylist(possibleName, songs, template, reason) PendingNewPlaylist(possibleName, songs, template, reason)
} else { } else {
T.w("Given song UIDs to create were invalid") L.w("Given song UIDs to create were invalid")
null null
} }
} }
@ -193,7 +193,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
template: String?, template: String?,
reason: PlaylistDecision.Rename.Reason reason: PlaylistDecision.Rename.Reason
) { ) {
T.d("Opening playlist $playlistUid to rename") L.d("Opening playlist $playlistUid to rename")
val playlist = musicRepository.userLibrary?.findPlaylist(playlistUid) val playlist = musicRepository.userLibrary?.findPlaylist(playlistUid)
val applySongs = val applySongs =
musicRepository.deviceLibrary?.let { applySongUids.mapNotNull(it::findSong) } musicRepository.deviceLibrary?.let { applySongUids.mapNotNull(it::findSong) }
@ -202,7 +202,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
if (playlist != null && applySongs != null) { if (playlist != null && applySongs != null) {
PendingRenamePlaylist(playlist, applySongs, template, reason) PendingRenamePlaylist(playlist, applySongs, template, reason)
} else { } else {
T.w("Given playlist UID to rename was invalid") L.w("Given playlist UID to rename was invalid")
null null
} }
} }
@ -213,12 +213,12 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
* @param playlistUid The [Music.UID] of the [Playlist] to export. * @param playlistUid The [Music.UID] of the [Playlist] to export.
*/ */
fun setPlaylistToExport(playlistUid: Music.UID) { fun setPlaylistToExport(playlistUid: Music.UID) {
T.d("Opening playlist $playlistUid to export") L.d("Opening playlist $playlistUid to export")
// TODO: Add this guard to the rest of the methods here // TODO: Add this guard to the rest of the methods here
if (_currentPlaylistToExport.value?.uid == playlistUid) return if (_currentPlaylistToExport.value?.uid == playlistUid) return
_currentPlaylistToExport.value = musicRepository.userLibrary?.findPlaylist(playlistUid) _currentPlaylistToExport.value = musicRepository.userLibrary?.findPlaylist(playlistUid)
if (_currentPlaylistToExport.value == null) { if (_currentPlaylistToExport.value == null) {
T.w("Given playlist UID to export was invalid") L.w("Given playlist UID to export was invalid")
} else { } else {
_currentExportConfig.value = DEFAULT_EXPORT_CONFIG _currentExportConfig.value = DEFAULT_EXPORT_CONFIG
} }
@ -230,7 +230,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
* @param exportConfig The new [ExportConfig] to use. * @param exportConfig The new [ExportConfig] to use.
*/ */
fun setExportConfig(exportConfig: ExportConfig) { fun setExportConfig(exportConfig: ExportConfig) {
T.d("Setting export config to $exportConfig") L.d("Setting export config to $exportConfig")
_currentExportConfig.value = exportConfig _currentExportConfig.value = exportConfig
} }
@ -240,10 +240,10 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
* @param playlistUid The [Music.UID] of the [Playlist] to delete. * @param playlistUid The [Music.UID] of the [Playlist] to delete.
*/ */
fun setPlaylistToDelete(playlistUid: Music.UID) { fun setPlaylistToDelete(playlistUid: Music.UID) {
T.d("Opening playlist $playlistUid to delete") L.d("Opening playlist $playlistUid to delete")
_currentPlaylistToDelete.value = musicRepository.userLibrary?.findPlaylist(playlistUid) _currentPlaylistToDelete.value = musicRepository.userLibrary?.findPlaylist(playlistUid)
if (_currentPlaylistToDelete.value == null) { if (_currentPlaylistToDelete.value == null) {
T.w("Given playlist UID to delete was invalid") L.w("Given playlist UID to delete was invalid")
} }
} }
@ -253,25 +253,25 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
* @param name The new user-inputted name, or null if not present. * @param name The new user-inputted name, or null if not present.
*/ */
fun updateChosenName(name: String?) { fun updateChosenName(name: String?) {
T.d("Updating chosen name to $name") L.d("Updating chosen name to $name")
_chosenName.value = _chosenName.value =
when { when {
name.isNullOrEmpty() -> { name.isNullOrEmpty() -> {
T.e("Chosen name is empty") L.e("Chosen name is empty")
ChosenName.Empty ChosenName.Empty
} }
name.isBlank() -> { name.isBlank() -> {
T.e("Chosen name is blank") L.e("Chosen name is blank")
ChosenName.Blank ChosenName.Blank
} }
else -> { else -> {
val trimmed = name.trim() val trimmed = name.trim()
val userLibrary = musicRepository.userLibrary val userLibrary = musicRepository.userLibrary
if (userLibrary != null && userLibrary.findPlaylist(trimmed) == null) { if (userLibrary != null && userLibrary.findPlaylist(trimmed) == null) {
T.d("Chosen name is valid") L.d("Chosen name is valid")
ChosenName.Valid(trimmed) ChosenName.Valid(trimmed)
} else { } else {
T.d("Chosen name already exists in library") L.d("Chosen name already exists in library")
ChosenName.AlreadyExists(trimmed) ChosenName.AlreadyExists(trimmed)
} }
} }
@ -284,19 +284,19 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
* @param songUids The [Music.UID]s of songs to add to a playlist. * @param songUids The [Music.UID]s of songs to add to a playlist.
*/ */
fun setSongsToAdd(songUids: Array<Music.UID>) { fun setSongsToAdd(songUids: Array<Music.UID>) {
T.d("Opening ${songUids.size} songs to add to a playlist") L.d("Opening ${songUids.size} songs to add to a playlist")
_currentSongsToAdd.value = _currentSongsToAdd.value =
musicRepository.deviceLibrary musicRepository.deviceLibrary
?.let { songUids.mapNotNull(it::findSong).ifEmpty { null } } ?.let { songUids.mapNotNull(it::findSong).ifEmpty { null } }
?.also(::refreshPlaylistChoices) ?.also(::refreshPlaylistChoices)
if (_currentSongsToAdd.value == null || songUids.size != _currentSongsToAdd.value?.size) { if (_currentSongsToAdd.value == null || songUids.size != _currentSongsToAdd.value?.size) {
T.w("Given song UIDs to add were (partially) invalid") L.w("Given song UIDs to add were (partially) invalid")
} }
} }
private fun refreshPlaylistChoices(songs: List<Song>) { private fun refreshPlaylistChoices(songs: List<Song>) {
val userLibrary = musicRepository.userLibrary ?: return val userLibrary = musicRepository.userLibrary ?: return
T.d("Refreshing playlist choices") L.d("Refreshing playlist choices")
_playlistAddChoices.value = _playlistAddChoices.value =
Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).playlists(userLibrary.playlists).map { Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).playlists(userLibrary.playlists).map {
val songSet = it.songs.toSet() val songSet = it.songs.toSet()

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A dialog allowing the name of a new playlist to be chosen before committing it to the database. * A dialog allowing the name of a new playlist to be chosen before committing it to the database.
@ -94,7 +94,7 @@ class RenamePlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNam
val default = val default =
pendingRenamePlaylist.template pendingRenamePlaylist.template
?: pendingRenamePlaylist.playlist.name.resolve(requireContext()) ?: pendingRenamePlaylist.playlist.name.resolve(requireContext())
T.d("Name input is not initialized, setting to $default") L.d("Name input is not initialized, setting to $default")
requireBinding().playlistName.setText(default) requireBinding().playlistName.setText(default)
initializedField = true initializedField = true
} }

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.music.metadata.Separators
import org.oxycblt.auxio.util.forEachWithTimeout import org.oxycblt.auxio.util.forEachWithTimeout
import org.oxycblt.auxio.util.sendWithTimeout import org.oxycblt.auxio.util.sendWithTimeout
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Organized music library information obtained from device storage. * Organized music library information obtained from device storage.
@ -147,7 +147,7 @@ class DeviceLibraryFactoryImpl @Inject constructor() : DeviceLibrary.Factory {
// UID is sufficient for something like this, and also prevents collisions from // UID is sufficient for something like this, and also prevents collisions from
// causing severe issues elsewhere. // causing severe issues elsewhere.
if (songGrouping.containsKey(song.uid)) { if (songGrouping.containsKey(song.uid)) {
T.w( L.w(
"Duplicate song found: ${song.path} " + "Duplicate song found: ${song.path} " +
"collides with ${unlikelyToBeNull(songGrouping[song.uid]).path}") "collides with ${unlikelyToBeNull(songGrouping[song.uid]).path}")
// We still want to say that we "processed" the song so that the user doesn't // We still want to say that we "processed" the song so that the user doesn't

View file

@ -125,8 +125,7 @@ class SongImpl(
.toSongCoverUri(), .toSongCoverUri(),
uri, uri,
it) it)
} } ?: Cover.External(requireNotNull(rawSong.albumMediaStoreId).toAlbumCoverUri())
?: Cover.External(requireNotNull(rawSong.albumMediaStoreId).toAlbumCoverUri())
/** /**
* The [RawAlbum] instances collated by the [Song]. This can be used to group [Song]s into an * The [RawAlbum] instances collated by the [Song]. This can be used to group [Song]s into an

View file

@ -26,7 +26,7 @@ import org.oxycblt.auxio.list.recycler.DialogRecyclerView
import org.oxycblt.auxio.music.fs.Path import org.oxycblt.auxio.music.fs.Path
import org.oxycblt.auxio.util.context import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.inflater
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* [RecyclerView.Adapter] that manages a list of [Path] music directory instances. * [RecyclerView.Adapter] that manages a list of [Path] music directory instances.
@ -55,7 +55,7 @@ class DirectoryAdapter(private val listener: Listener) :
*/ */
fun add(path: Path) { fun add(path: Path) {
if (_dirs.contains(path)) return if (_dirs.contains(path)) return
T.d("Adding $path") L.d("Adding $path")
_dirs.add(path) _dirs.add(path)
notifyItemInserted(_dirs.lastIndex) notifyItemInserted(_dirs.lastIndex)
} }
@ -66,7 +66,7 @@ class DirectoryAdapter(private val listener: Listener) :
* @param path The [Path] instances to add. * @param path The [Path] instances to add.
*/ */
fun addAll(path: List<Path>) { fun addAll(path: List<Path>) {
T.d("Adding ${path.size} directories") L.d("Adding ${path.size} directories")
val oldLastIndex = path.lastIndex val oldLastIndex = path.lastIndex
_dirs.addAll(path) _dirs.addAll(path)
notifyItemRangeInserted(oldLastIndex, path.size) notifyItemRangeInserted(oldLastIndex, path.size)
@ -78,7 +78,7 @@ class DirectoryAdapter(private val listener: Listener) :
* @param path The [Path] to remove. Must exist in the list. * @param path The [Path] to remove. Must exist in the list.
*/ */
fun remove(path: Path) { fun remove(path: Path) {
T.d("Removing $path") L.d("Removing $path")
val idx = _dirs.indexOf(path) val idx = _dirs.indexOf(path)
_dirs.removeAt(idx) _dirs.removeAt(idx)
notifyItemRemoved(idx) notifyItemRemoved(idx)

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.music.fs.DocumentPathFactory
import org.oxycblt.auxio.music.fs.Path import org.oxycblt.auxio.music.fs.Path
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Dialog that manages the music dirs setting. * Dialog that manages the music dirs setting.
@ -62,7 +62,7 @@ class MusicDirsDialog :
.setPositiveButton(R.string.lbl_save) { _, _ -> .setPositiveButton(R.string.lbl_save) { _, _ ->
val newDirs = MusicDirectories(dirAdapter.dirs, isUiModeInclude(requireBinding())) val newDirs = MusicDirectories(dirAdapter.dirs, isUiModeInclude(requireBinding()))
if (musicSettings.musicDirs != newDirs) { if (musicSettings.musicDirs != newDirs) {
T.d("Committing changes") L.d("Committing changes")
musicSettings.musicDirs = newDirs musicSettings.musicDirs = newDirs
} }
} }
@ -76,7 +76,7 @@ class MusicDirsDialog :
binding.dirsAdd.apply { binding.dirsAdd.apply {
ViewCompat.setTooltipText(this, contentDescription) ViewCompat.setTooltipText(this, contentDescription)
setOnClickListener { setOnClickListener {
T.d("Opening launcher") L.d("Opening launcher")
val launcher = val launcher =
requireNotNull(openDocumentTreeLauncher) { requireNotNull(openDocumentTreeLauncher) {
"Document tree launcher was not available" "Document tree launcher was not available"
@ -150,7 +150,7 @@ class MusicDirsDialog :
private fun addDocumentTreeUriToDirs(uri: Uri?) { private fun addDocumentTreeUriToDirs(uri: Uri?) {
if (uri == null) { if (uri == null) {
// A null URI means that the user left the file picker without picking a directory // A null URI means that the user left the file picker without picking a directory
T.d("No URI given (user closed the dialog)") L.d("No URI given (user closed the dialog)")
return return
} }

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.music.fs.Components
import org.oxycblt.auxio.music.fs.DocumentPathFactory import org.oxycblt.auxio.music.fs.DocumentPathFactory
import org.oxycblt.auxio.music.fs.Path import org.oxycblt.auxio.music.fs.Path
import org.oxycblt.auxio.music.fs.contentResolverSafe import org.oxycblt.auxio.music.fs.contentResolverSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Generic playlist file importing abstraction. * Generic playlist file importing abstraction.
@ -108,7 +108,7 @@ constructor(
return ImportedPlaylist(newName, imported.paths) return ImportedPlaylist(newName, imported.paths)
} }
} catch (e: Exception) { } catch (e: Exception) {
T.e("Failed to import playlist: $e") L.e("Failed to import playlist: $e")
null null
} }
} }
@ -124,7 +124,7 @@ constructor(
return try { return try {
val outputStream = context.contentResolverSafe.openOutputStream(uri) val outputStream = context.contentResolverSafe.openOutputStream(uri)
if (outputStream == null) { if (outputStream == null) {
T.e("Failed to export playlist: Could not open output stream") L.e("Failed to export playlist: Could not open output stream")
return false return false
} }
outputStream.use { outputStream.use {
@ -132,7 +132,7 @@ constructor(
true true
} }
} catch (e: Exception) { } catch (e: Exception) {
T.e("Failed to export playlist: $e") L.e("Failed to export playlist: $e")
false false
} }
} }

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.music.fs.VolumeManager
import org.oxycblt.auxio.music.metadata.correctWhitespace import org.oxycblt.auxio.music.metadata.correctWhitespace
import org.oxycblt.auxio.music.resolveNames import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Minimal M3U file format implementation. * Minimal M3U file format implementation.
@ -116,7 +116,7 @@ constructor(
} }
if (path == null) { if (path == null) {
T.e("Expected a path, instead got an EOF") L.e("Expected a path, instead got an EOF")
break@consumeFile break@consumeFile
} }
@ -261,7 +261,7 @@ constructor(
} }
commonIndex == components.size -> { commonIndex == components.size -> {
// The working directory is deeper in the path, backtrack. // The working directory is deeper in the path, backtrack.
for (i in 0..<workingDirectory.components.size - commonIndex) { for (i in 0 ..< workingDirectory.components.size - commonIndex) {
relativeComponents = relativeComponents.child("..") relativeComponents = relativeComponents.child("..")
} }
} }
@ -272,7 +272,7 @@ constructor(
} }
else -> { else -> {
// The paths are siblings. Backtrack and append as needed. // The paths are siblings. Backtrack and append as needed.
for (i in 0..<workingDirectory.components.size - commonIndex) { for (i in 0 ..< workingDirectory.components.size - commonIndex) {
relativeComponents = relativeComponents.child("..") relativeComponents = relativeComponents.child("..")
} }
relativeComponents = relativeComponents.child(depth(commonIndex)) relativeComponents = relativeComponents.child(depth(commonIndex))

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.music.info.Date
import org.oxycblt.auxio.music.metadata.parseId3v2PositionField import org.oxycblt.auxio.music.metadata.parseId3v2PositionField
import org.oxycblt.auxio.music.metadata.transformPositionField import org.oxycblt.auxio.music.metadata.transformPositionField
import org.oxycblt.auxio.util.sendWithTimeout import org.oxycblt.auxio.util.sendWithTimeout
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* The layer that loads music from the [MediaStore] database. This is an intermediate step in the * The layer that loads music from the [MediaStore] database. This is an intermediate step in the
@ -127,7 +127,7 @@ private class MediaStoreExtractorImpl(
// Filter out audio that is not music, if enabled. // Filter out audio that is not music, if enabled.
if (constraints.excludeNonMusic) { if (constraints.excludeNonMusic) {
T.d("Excluding non-music") L.d("Excluding non-music")
uniSelector += " AND ${MediaStore.Audio.AudioColumns.IS_MUSIC}=1" uniSelector += " AND ${MediaStore.Audio.AudioColumns.IS_MUSIC}=1"
} }
@ -136,10 +136,10 @@ private class MediaStoreExtractorImpl(
val pathSelector = val pathSelector =
mediaStorePathInterpreterFactory.createSelector(constraints.musicDirs.dirs) mediaStorePathInterpreterFactory.createSelector(constraints.musicDirs.dirs)
if (pathSelector != null) { if (pathSelector != null) {
T.d("Must select for directories") L.d("Must select for directories")
uniSelector += " AND " uniSelector += " AND "
if (!constraints.musicDirs.shouldInclude) { if (!constraints.musicDirs.shouldInclude) {
T.d("Excluding directories in selector") L.d("Excluding directories in selector")
// Without a NOT, the query will be restricted to the specified paths, resulting // Without a NOT, the query will be restricted to the specified paths, resulting
// in the "Include" mode. With a NOT, the specified paths will not be included, // in the "Include" mode. With a NOT, the specified paths will not be included,
// resulting in the "Exclude" mode. // resulting in the "Exclude" mode.
@ -151,7 +151,7 @@ private class MediaStoreExtractorImpl(
} }
// Now we can actually query MediaStore. // Now we can actually query MediaStore.
T.d( L.d(
"Starting song query [proj=${projection.toList()}, selector=$uniSelector, args=$uniArgs]") "Starting song query [proj=${projection.toList()}, selector=$uniSelector, args=$uniArgs]")
val cursor = val cursor =
context.contentResolverSafe.safeQuery( context.contentResolverSafe.safeQuery(
@ -159,7 +159,7 @@ private class MediaStoreExtractorImpl(
projection, projection,
uniSelector, uniSelector,
uniArgs.toTypedArray()) uniArgs.toTypedArray())
T.d("Successfully queried for ${cursor.count} songs") L.d("Successfully queried for ${cursor.count} songs")
val genreNamesMap = mutableMapOf<Long, String>() val genreNamesMap = mutableMapOf<Long, String>()
@ -194,8 +194,8 @@ private class MediaStoreExtractorImpl(
} }
} }
T.d("Read ${genreNamesMap.values.distinct().size} genres from MediaStore") L.d("Read ${genreNamesMap.values.distinct().size} genres from MediaStore")
T.d("Finished initialization in ${System.currentTimeMillis() - start}ms") L.d("Finished initialization in ${System.currentTimeMillis() - start}ms")
return QueryImpl( return QueryImpl(
cursor, cursor,
mediaStorePathInterpreterFactory.wrap(cursor), mediaStorePathInterpreterFactory.wrap(cursor),

View file

@ -21,7 +21,7 @@ package org.oxycblt.auxio.music.fs
import android.database.Cursor import android.database.Cursor
import android.os.Build import android.os.Build
import android.provider.MediaStore import android.provider.MediaStore
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Wrapper around a [Cursor] that interprets path information on a per-API/manufacturer basis. * Wrapper around a [Cursor] that interprets path information on a per-API/manufacturer basis.
@ -112,7 +112,7 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
} }
} }
T.e("Could not find volume for $data [tried: ${volumes.map { it.components }}]") L.e("Could not find volume for $data [tried: ${volumes.map { it.components }}]")
return null return null
} }
@ -181,7 +181,7 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
val displayName = cursor.getString(displayNameIndex) val displayName = cursor.getString(displayNameIndex)
val volume = volumes.find { it.mediaStoreName == volumeName } val volume = volumes.find { it.mediaStoreName == volumeName }
if (volume == null) { if (volume == null) {
T.e( L.e(
"Could not find volume for $volumeName:$relativePath/$displayName [tried: ${volumes.map { it.mediaStoreName }}]") "Could not find volume for $volumeName:$relativePath/$displayName [tried: ${volumes.map { it.mediaStoreName }}]")
return null return null
} }

View file

@ -25,7 +25,7 @@ import kotlin.math.max
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.inRangeOrNull import org.oxycblt.auxio.util.inRangeOrNull
import org.oxycblt.auxio.util.positiveOrNull import org.oxycblt.auxio.util.positiveOrNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* An ISO-8601/RFC 3339 Date. * An ISO-8601/RFC 3339 Date.
@ -64,7 +64,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
try { try {
format.parse("$year-$month") format.parse("$year-$month")
} catch (e: ParseException) { } catch (e: ParseException) {
T.e("Unable to parse fine-grained date: $e") L.e("Unable to parse fine-grained date: $e")
return null return null
} }

View file

@ -62,8 +62,7 @@ sealed interface Name : Comparable<Name> {
sortTokens sortTokens
.firstOrNull() .firstOrNull()
?.run { collationKey.sourceString.firstOrNull() } ?.run { collationKey.sourceString.firstOrNull() }
?.let { if (it.isDigit()) "#" else it.uppercase() } ?.let { if (it.isDigit()) "#" else it.uppercase() } ?: "?"
?: "?"
final override fun resolve(context: Context) = raw final override fun resolve(context: Context) = raw

View file

@ -25,7 +25,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.fs.MimeType import org.oxycblt.auxio.music.fs.MimeType
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* The properties of a [Song]'s file. * The properties of a [Song]'s file.
@ -73,8 +73,8 @@ constructor(@ApplicationContext private val context: Context) : AudioProperties.
// Can feasibly fail with invalid file formats. Note that this isn't considered // Can feasibly fail with invalid file formats. Note that this isn't considered
// an error condition in the UI, as there is still plenty of other song information // an error condition in the UI, as there is still plenty of other song information
// that we can show. // that we can show.
T.w("Unable to extract song attributes.") L.w("Unable to extract song attributes.")
T.w(e.stackTraceToString()) L.w(e.stackTraceToString())
return AudioProperties(null, null, song.mimeType) return AudioProperties(null, null, song.mimeType)
} }
@ -90,7 +90,7 @@ constructor(@ApplicationContext private val context: Context) : AudioProperties.
// Convert bytes-per-second to kilobytes-per-second. // Convert bytes-per-second to kilobytes-per-second.
format.getInteger(MediaFormat.KEY_BIT_RATE) / 1000 format.getInteger(MediaFormat.KEY_BIT_RATE) / 1000
} catch (e: NullPointerException) { } catch (e: NullPointerException) {
T.d("Unable to extract bit rate field") L.d("Unable to extract bit rate field")
null null
} }
@ -98,7 +98,7 @@ constructor(@ApplicationContext private val context: Context) : AudioProperties.
try { try {
format.getInteger(MediaFormat.KEY_SAMPLE_RATE) format.getInteger(MediaFormat.KEY_SAMPLE_RATE)
} catch (e: NullPointerException) { } catch (e: NullPointerException) {
T.e("Unable to extract sample rate field") L.e("Unable to extract sample rate field")
null null
} }
@ -108,13 +108,13 @@ constructor(@ApplicationContext private val context: Context) : AudioProperties.
try { try {
format.getString(MediaFormat.KEY_MIME) format.getString(MediaFormat.KEY_MIME)
} catch (e: NullPointerException) { } catch (e: NullPointerException) {
T.e("Unable to extract mime type field") L.e("Unable to extract mime type field")
null null
} }
extractor.release() extractor.release()
T.d("Finished extracting audio properties") L.d("Finished extracting audio properties")
return AudioProperties( return AudioProperties(
bitrate, bitrate,

View file

@ -30,7 +30,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogSeparatorsBinding import org.oxycblt.auxio.databinding.DialogSeparatorsBinding
import org.oxycblt.auxio.music.MusicSettings import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingMaterialDialogFragment] that allows the user to configure the separator characters * A [ViewBindingMaterialDialogFragment] that allows the user to configure the separator characters
@ -76,7 +76,7 @@ class SeparatorsDialog : ViewBindingMaterialDialogFragment<DialogSeparatorsBindi
Separators.SLASH -> binding.separatorSlash.isChecked = true Separators.SLASH -> binding.separatorSlash.isChecked = true
Separators.PLUS -> binding.separatorPlus.isChecked = true Separators.PLUS -> binding.separatorPlus.isChecked = true
Separators.AND -> binding.separatorAnd.isChecked = true Separators.AND -> binding.separatorAnd.isChecked = true
else -> T.w("Unexpected separator in settings data") else -> L.w("Unexpected separator in settings data")
} }
} }
} }

View file

@ -42,7 +42,7 @@ import org.oxycblt.auxio.music.device.RawSong
import org.oxycblt.auxio.music.fs.toAudioUri import org.oxycblt.auxio.music.fs.toAudioUri
import org.oxycblt.auxio.util.forEachWithTimeout import org.oxycblt.auxio.util.forEachWithTimeout
import org.oxycblt.auxio.util.sendWithTimeout import org.oxycblt.auxio.util.sendWithTimeout
import timber.log.Timber as T import timber.log.Timber as L
class TagExtractor class TagExtractor
@Inject @Inject
@ -66,7 +66,7 @@ constructor(private val mediaSourceFactory: Factory, private val tagInterpreter:
songsIn++ songsIn++
} }
T.d("All incomplete songs exhausted, starting cleanup loop") L.d("All incomplete songs exhausted, starting cleanup loop")
while (!worker.idle()) { while (!worker.idle()) {
val completeRawSong = worker.pull() val completeRawSong = worker.pull()
if (completeRawSong != null) { if (completeRawSong != null) {
@ -152,8 +152,8 @@ private class MetadataWorker(
try { try {
tagInterpreter.interpret(job.rawSong, job.future.get()) tagInterpreter.interpret(job.rawSong, job.future.get())
} catch (e: Exception) { } catch (e: Exception) {
T.e("Failed to extract metadata") L.e("Failed to extract metadata")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
} }
jobs[i] = null jobs[i] = null
return job.rawSong return job.rawSong
@ -176,7 +176,7 @@ private class MetadataWorker(
mediaSource = currentMediaSource mediaSource = currentMediaSource
mediaSourceCaller = currentMediaSourceCaller mediaSourceCaller = currentMediaSourceCaller
} else { } else {
T.d("new media source yahoo") L.d("new media source yahoo")
mediaSource = mediaSourceFactory.createMediaSource(job.mediaItem) mediaSource = mediaSourceFactory.createMediaSource(job.mediaItem)
mediaSourceCaller = MediaSourceCaller(job) mediaSourceCaller = MediaSourceCaller(job)
mediaSource.prepareSource( mediaSource.prepareSource(
@ -193,8 +193,8 @@ private class MetadataWorker(
mediaPeriod.maybeThrowPrepareError() mediaPeriod.maybeThrowPrepareError()
} }
} catch (e: Exception) { } catch (e: Exception) {
T.e("Failed to extract MediaSource") L.e("Failed to extract MediaSource")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
job.mediaPeriod?.let(mediaSource::releasePeriod) job.mediaPeriod?.let(mediaSource::releasePeriod)
mediaSource.releaseSource(mediaSourceCaller) mediaSource.releaseSource(mediaSourceCaller)
job.future.setException(e) job.future.setException(e)
@ -247,7 +247,7 @@ private class MetadataWorker(
// Ignore dynamic updates. // Ignore dynamic updates.
return return
} }
T.d("yay source created") L.d("yay source created")
mediaPeriodCreated = true mediaPeriodCreated = true
val mediaPeriod = val mediaPeriod =
source.createPeriod( source.createPeriod(

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.image.extractor.CoverExtractor
import org.oxycblt.auxio.music.device.RawSong import org.oxycblt.auxio.music.device.RawSong
import org.oxycblt.auxio.music.info.Date import org.oxycblt.auxio.music.info.Date
import org.oxycblt.auxio.util.nonZeroOrNull import org.oxycblt.auxio.util.nonZeroOrNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* An processing abstraction over the [MetadataRetriever] and [TextTags] workflow that operates on * An processing abstraction over the [MetadataRetriever] and [TextTags] workflow that operates on
@ -82,19 +82,19 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
// val gain = // val gain =
// (((header[16]).toInt() and 0xFF) or ((header[17].toInt() shl 8))) // (((header[16]).toInt() and 0xFF) or ((header[17].toInt() shl 8)))
// .R128ToLUFS18() // .R128ToLUFS18()
// T.d("Obtained opus base gain: $gain dB") // L.d("Obtained opus base gain: $gain dB")
// if (gain != 0f) { // if (gain != 0f) {
// T.d("Applying opus base gain") // L.d("Applying opus base gain")
// rawSong.replayGainTrackAdjustment = // rawSong.replayGainTrackAdjustment =
// (rawSong.replayGainTrackAdjustment ?: 0f) + gain // (rawSong.replayGainTrackAdjustment ?: 0f) + gain
// rawSong.replayGainAlbumAdjustment = // rawSong.replayGainAlbumAdjustment =
// (rawSong.replayGainAlbumAdjustment ?: 0f) + gain // (rawSong.replayGainAlbumAdjustment ?: 0f) + gain
// } else { // } else {
// T.d("Ignoring opus base gain") // L.d("Ignoring opus base gain")
// } // }
// } // }
} else { } else {
T.d("No metadata could be extracted for ${rawSong.name}") L.d("No metadata could be extracted for ${rawSong.name}")
} }
} }
@ -127,8 +127,8 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
// isn't known? // isn't known?
(textFrames["TDOR"]?.run { Date.from(first()) } (textFrames["TDOR"]?.run { Date.from(first()) }
?: textFrames["TDRC"]?.run { Date.from(first()) } ?: textFrames["TDRC"]?.run { Date.from(first()) }
?: textFrames["TDRL"]?.run { Date.from(first()) } ?: textFrames["TDRL"]?.run { Date.from(first()) }
?: parseId3v23Date(textFrames)) ?: parseId3v23Date(textFrames))
?.let { rawSong.date = it } ?.let { rawSong.date = it }
// Album // Album
@ -138,7 +138,8 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
textFrames["TALB"]?.let { rawSong.albumName = it.first() } textFrames["TALB"]?.let { rawSong.albumName = it.first() }
textFrames["TSOA"]?.let { rawSong.albumSortName = it.first() } textFrames["TSOA"]?.let { rawSong.albumSortName = it.first() }
(textFrames["TXXX:musicbrainz album type"] (textFrames["TXXX:musicbrainz album type"]
?: textFrames["TXXX:releasetype"] ?: ?: textFrames["TXXX:releasetype"]
?:
// This is a non-standard iTunes extension // This is a non-standard iTunes extension
textFrames["GRP1"]) textFrames["GRP1"])
?.let { rawSong.releaseTypes = it } ?.let { rawSong.releaseTypes = it }
@ -151,9 +152,11 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
rawSong.artistNames = it rawSong.artistNames = it
} }
(textFrames["TXXX:artistssort"] (textFrames["TXXX:artistssort"]
?: textFrames["TXXX:artists_sort"] ?: textFrames["TXXX:artists sort"] ?: textFrames["TXXX:artists_sort"]
?: textFrames["TSOP"] ?: textFrames["artistsort"] ?: textFrames["TXXX:artists sort"]
?: textFrames["TXXX:artist sort"]) ?: textFrames["TSOP"]
?: textFrames["artistsort"]
?: textFrames["TXXX:artist sort"])
?.let { rawSong.artistSortNames = it } ?.let { rawSong.artistSortNames = it }
// Album artist // Album artist
@ -161,15 +164,19 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
?: textFrames["TXXX:musicbrainz_albumartistid"]) ?: textFrames["TXXX:musicbrainz_albumartistid"])
?.let { rawSong.albumArtistMusicBrainzIds = it } ?.let { rawSong.albumArtistMusicBrainzIds = it }
(textFrames["TXXX:albumartists"] (textFrames["TXXX:albumartists"]
?: textFrames["TXXX:album_artists"] ?: textFrames["TXXX:album artists"] ?: textFrames["TXXX:album_artists"]
?: textFrames["TPE2"] ?: textFrames["TXXX:albumartist"] ?: textFrames["TXXX:album artists"]
?: textFrames["TXXX:album artist"]) ?: textFrames["TPE2"]
?: textFrames["TXXX:albumartist"]
?: textFrames["TXXX:album artist"])
?.let { rawSong.albumArtistNames = it } ?.let { rawSong.albumArtistNames = it }
(textFrames["TXXX:albumartistssort"] (textFrames["TXXX:albumartistssort"]
?: textFrames["TXXX:albumartists_sort"] ?: textFrames["TXXX:albumartists sort"] ?: textFrames["TXXX:albumartists_sort"]
?: textFrames["TXXX:albumartistsort"] ?: textFrames["TXXX:albumartists sort"]
?: textFrames["TXXX:albumartistsort"]
// This is a non-standard iTunes extension // This is a non-standard iTunes extension
?: textFrames["TSO2"] ?: textFrames["TXXX:album artist sort"]) ?: textFrames["TSO2"]
?: textFrames["TXXX:album artist sort"])
?.let { rawSong.albumArtistSortNames = it } ?.let { rawSong.albumArtistSortNames = it }
// Genre // Genre
@ -177,7 +184,7 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
// Compilation Flag // Compilation Flag
(textFrames["TCMP"] // This is a non-standard itunes extension (textFrames["TCMP"] // This is a non-standard itunes extension
?: textFrames["TXXX:compilation"] ?: textFrames["TXXX:itunescompilation"]) ?: textFrames["TXXX:compilation"] ?: textFrames["TXXX:itunescompilation"])
?.let { ?.let {
// Ignore invalid instances of this tag // Ignore invalid instances of this tag
if (it.size != 1 || it[0] != "1") return@let if (it.size != 1 || it[0] != "1") return@let
@ -201,7 +208,8 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
// is present. // is present.
val year = val year =
textFrames["TORY"]?.run { first().toIntOrNull() } textFrames["TORY"]?.run { first().toIntOrNull() }
?: textFrames["TYER"]?.run { first().toIntOrNull() } ?: return null ?: textFrames["TYER"]?.run { first().toIntOrNull() }
?: return null
val tdat = textFrames["TDAT"] val tdat = textFrames["TDAT"]
return if (tdat != null && tdat.first().length == 4 && tdat.first().isDigitsOnly()) { return if (tdat != null && tdat.first().length == 4 && tdat.first().isDigitsOnly()) {
@ -258,7 +266,7 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
// date tag that android supports, so it must be 15 years old or more!) // date tag that android supports, so it must be 15 years old or more!)
(comments["originaldate"]?.run { Date.from(first()) } (comments["originaldate"]?.run { Date.from(first()) }
?: comments["date"]?.run { Date.from(first()) } ?: comments["date"]?.run { Date.from(first()) }
?: comments["year"]?.run { Date.from(first()) }) ?: comments["year"]?.run { Date.from(first()) })
?.let { rawSong.date = it } ?.let { rawSong.date = it }
// Album // Album
@ -277,8 +285,10 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
} }
(comments["artists"] ?: comments["artist"])?.let { rawSong.artistNames = it } (comments["artists"] ?: comments["artist"])?.let { rawSong.artistNames = it }
(comments["artistssort"] (comments["artistssort"]
?: comments["artists_sort"] ?: comments["artists sort"] ?: comments["artistsort"] ?: comments["artists_sort"]
?: comments["artist sort"]) ?: comments["artists sort"]
?: comments["artistsort"]
?: comments["artist sort"])
?.let { rawSong.artistSortNames = it } ?.let { rawSong.artistSortNames = it }
// Album artist // Album artist
@ -286,12 +296,16 @@ class TagInterpreterImpl @Inject constructor(private val coverExtractor: CoverEx
rawSong.albumArtistMusicBrainzIds = it rawSong.albumArtistMusicBrainzIds = it
} }
(comments["albumartists"] (comments["albumartists"]
?: comments["album_artists"] ?: comments["album artists"] ?: comments["albumartist"] ?: comments["album_artists"]
?: comments["album artist"]) ?: comments["album artists"]
?: comments["albumartist"]
?: comments["album artist"])
?.let { rawSong.albumArtistNames = it } ?.let { rawSong.albumArtistNames = it }
(comments["albumartistssort"] (comments["albumartistssort"]
?: comments["albumartists_sort"] ?: comments["albumartists sort"] ?: comments["albumartists_sort"]
?: comments["albumartistsort"] ?: comments["album artist sort"]) ?: comments["albumartists sort"]
?: comments["albumartistsort"]
?: comments["album artist sort"])
?.let { rawSong.albumArtistSortNames = it } ?.let { rawSong.albumArtistSortNames = it }
// Genre // Genre

View file

@ -170,8 +170,8 @@ private fun String.parseId3v1Genre(): String? {
// try to index the genre table with such. // try to index the genre table with such.
val numeric = val numeric =
toIntOrNull() toIntOrNull()
// Not a numeric value, try some other fixed values. // Not a numeric value, try some other fixed values.
?: return when (this) { ?: return when (this) {
// CR and RX are not technically ID3v1, but are formatted similarly to a plain // CR and RX are not technically ID3v1, but are formatted similarly to a plain
// number. // number.
"CR" -> "Cover" "CR" -> "Cover"

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicSettings import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.util.getSystemServiceCompat import org.oxycblt.auxio.util.getSystemServiceCompat
import timber.log.Timber as T import timber.log.Timber as L
class Indexer class Indexer
private constructor( private constructor(
@ -117,7 +117,7 @@ private constructor(
} }
} else if (musicSettings.shouldBeObserving) { } else if (musicSettings.shouldBeObserving) {
// Not observing and done loading, exit foreground. // Not observing and done loading, exit foreground.
T.d("Exiting foreground") L.d("Exiting foreground")
post(observingNotification) post(observingNotification)
} else { } else {
post(null) post(null)
@ -125,7 +125,7 @@ private constructor(
} }
override fun requestIndex(withCache: Boolean) { override fun requestIndex(withCache: Boolean) {
T.d("Starting new indexing job (previous=${currentIndexJob?.hashCode()})") L.d("Starting new indexing job (previous=${currentIndexJob?.hashCode()})")
// Cancel the previous music loading job. // Cancel the previous music loading job.
currentIndexJob?.cancel() currentIndexJob?.cancel()
// Start a new music loading job on a co-routine. // Start a new music loading job on a co-routine.
@ -146,7 +146,7 @@ private constructor(
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
val deviceLibrary = musicRepository.deviceLibrary ?: return val deviceLibrary = musicRepository.deviceLibrary ?: return
T.d("Music changed, updating shared objects") L.d("Music changed, updating shared objects")
// Wipe possibly-invalidated outdated covers // Wipe possibly-invalidated outdated covers
imageLoader.memoryCache?.clear() imageLoader.memoryCache?.clear()
// Clear invalid models from PlaybackStateManager. This is not connected // Clear invalid models from PlaybackStateManager. This is not connected
@ -175,7 +175,7 @@ private constructor(
// setting changed. In such a case, the state will still be updated when // setting changed. In such a case, the state will still be updated when
// the music loading process ends. // the music loading process ends.
if (musicRepository.indexingState == null) { if (musicRepository.indexingState == null) {
T.d("Not loading, updating idle session") L.d("Not loading, updating idle session")
foregroundListener.updateForeground(ForegroundListener.Change.INDEXER) foregroundListener.updateForeground(ForegroundListener.Change.INDEXER)
} }
} }
@ -184,7 +184,7 @@ private constructor(
private fun PowerManager.WakeLock.acquireSafe() { private fun PowerManager.WakeLock.acquireSafe() {
// Avoid unnecessary acquire calls. // Avoid unnecessary acquire calls.
if (!wakeLock.isHeld) { if (!wakeLock.isHeld) {
T.d("Acquiring wake lock") L.d("Acquiring wake lock")
// Time out after a minute, which is the average music loading time for a medium-sized // Time out after a minute, which is the average music loading time for a medium-sized
// library. If this runs out, we will re-request the lock, and if music loading is // library. If this runs out, we will re-request the lock, and if music loading is
// shorter than the timeout, it will be released early. // shorter than the timeout, it will be released early.
@ -196,7 +196,7 @@ private constructor(
private fun PowerManager.WakeLock.releaseSafe() { private fun PowerManager.WakeLock.releaseSafe() {
// Avoid unnecessary release calls. // Avoid unnecessary release calls.
if (wakeLock.isHeld) { if (wakeLock.isHeld) {
T.d("Releasing wake lock") L.d("Releasing wake lock")
release() release()
} }
} }

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.IndexingProgress import org.oxycblt.auxio.music.IndexingProgress
import org.oxycblt.auxio.util.newMainPendingIntent import org.oxycblt.auxio.util.newMainPendingIntent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A dynamic [ForegroundServiceNotification] that shows the current music loading state. * A dynamic [ForegroundServiceNotification] that shows the current music loading state.
@ -66,7 +66,7 @@ class IndexingNotification(private val context: Context) :
// Indeterminate state, use a vaguer description and in-determinate progress. // Indeterminate state, use a vaguer description and in-determinate progress.
// These events are not very frequent, and thus we don't need to safeguard // These events are not very frequent, and thus we don't need to safeguard
// against rate limiting. // against rate limiting.
T.d("Updating state to $progress") L.d("Updating state to $progress")
lastUpdateTime = -1 lastUpdateTime = -1
setContentText(context.getString(R.string.lng_indexing)) setContentText(context.getString(R.string.lng_indexing))
setProgress(0, 0, true) setProgress(0, 0, true)
@ -81,7 +81,7 @@ class IndexingNotification(private val context: Context) :
return false return false
} }
lastUpdateTime = SystemClock.elapsedRealtime() lastUpdateTime = SystemClock.elapsedRealtime()
T.d("Updating state to $progress") L.d("Updating state to $progress")
setContentText( setContentText(
context.getString(R.string.fmt_indexing, progress.current, progress.total)) context.getString(R.string.fmt_indexing, progress.current, progress.total))
setProgress(progress.total, progress.current, false) setProgress(progress.total, progress.current, false)

View file

@ -119,8 +119,7 @@ private constructor(
is MediaSessionUID.SingleItem -> is MediaSessionUID.SingleItem ->
musicRepository.find(uid.uid)?.let { musicRepository.find(it.uid) } musicRepository.find(uid.uid)?.let { musicRepository.find(it.uid) }
null -> null null -> null
} } ?: return null
?: return null
return when (music) { return when (music) {
is Album -> music.toMediaItem(context) is Album -> music.toMediaItem(context)

View file

@ -23,6 +23,7 @@ import android.os.Bundle
import android.support.v4.media.MediaBrowserCompat.MediaItem import android.support.v4.media.MediaBrowserCompat.MediaItem
import androidx.media.MediaBrowserServiceCompat.BrowserRoot import androidx.media.MediaBrowserServiceCompat.BrowserRoot
import androidx.media.MediaBrowserServiceCompat.Result import androidx.media.MediaBrowserServiceCompat.Result
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -30,7 +31,7 @@ import kotlinx.coroutines.launch
import org.oxycblt.auxio.ForegroundListener import org.oxycblt.auxio.ForegroundListener
import org.oxycblt.auxio.ForegroundServiceNotification import org.oxycblt.auxio.ForegroundServiceNotification
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
import javax.inject.Inject import timber.log.Timber as L
class MusicServiceFragment class MusicServiceFragment
@Inject @Inject
@ -122,11 +123,11 @@ constructor(
try { try {
val result = body() val result = body()
if (result == null) { if (result == null) {
T.w("Result is null") L.w("Result is null")
} }
sendResult(result) sendResult(result)
} catch (e: Exception) { } catch (e: Exception) {
T.d("Error while dispatching: $e") L.d("Error while dispatching: $e")
sendResult(null) sendResult(null)
} }
} }
@ -137,11 +138,11 @@ constructor(
try { try {
val result = body() val result = body()
if (result == null) { if (result == null) {
T.w("Result is null") L.w("Result is null")
} }
sendResult(result) sendResult(result)
} catch (e: Exception) { } catch (e: Exception) {
T.d("Error while dispatching: $e") L.d("Error while dispatching: $e")
sendResult(null) sendResult(null)
} }
} }

View file

@ -28,7 +28,7 @@ import javax.inject.Inject
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicSettings import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.music.fs.contentResolverSafe import org.oxycblt.auxio.music.fs.contentResolverSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ContentObserver] that observes the [MediaStore] music database for changes, a behavior known * A [ContentObserver] that observes the [MediaStore] music database for changes, a behavior known
@ -68,7 +68,7 @@ constructor(
// Check here if we should even start a reindex. This is much less bug-prone than // Check here if we should even start a reindex. This is much less bug-prone than
// registering and de-registering this component as this setting changes. // registering and de-registering this component as this setting changes.
if (musicSettings.shouldBeObserving) { if (musicSettings.shouldBeObserving) {
T.d("MediaStore changed, starting re-index") L.d("MediaStore changed, starting re-index")
musicRepository.requestIndex(true) musicRepository.requestIndex(true)
} }
} }

View file

@ -26,7 +26,7 @@ import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.device.DeviceLibrary import org.oxycblt.auxio.music.device.DeviceLibrary
import org.oxycblt.auxio.music.info.Name import org.oxycblt.auxio.music.info.Name
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Organized library information controlled by the user. * Organized library information controlled by the user.
@ -143,10 +143,10 @@ class UserLibraryFactoryImpl @Inject constructor(private val playlistDao: Playli
override suspend fun query() = override suspend fun query() =
try { try {
val rawPlaylists = playlistDao.readRawPlaylists() val rawPlaylists = playlistDao.readRawPlaylists()
T.d("Successfully read ${rawPlaylists.size} playlists") L.d("Successfully read ${rawPlaylists.size} playlists")
rawPlaylists rawPlaylists
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to read playlists: $e") L.e("Unable to read playlists: $e")
listOf() listOf()
} }
@ -192,11 +192,11 @@ private class UserLibraryImpl(
return try { return try {
playlistDao.insertPlaylist(rawPlaylist) playlistDao.insertPlaylist(rawPlaylist)
T.d("Successfully created playlist $name with ${songs.size} songs") L.d("Successfully created playlist $name with ${songs.size} songs")
playlistImpl playlistImpl
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to create playlist $name with ${songs.size} songs") L.e("Unable to create playlist $name with ${songs.size} songs")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
synchronized(this) { playlistMap.remove(playlistImpl.uid) } synchronized(this) { playlistMap.remove(playlistImpl.uid) }
null null
} }
@ -211,11 +211,11 @@ private class UserLibraryImpl(
return try { return try {
playlistDao.replacePlaylistInfo(PlaylistInfo(playlist.uid, name)) playlistDao.replacePlaylistInfo(PlaylistInfo(playlist.uid, name))
T.d("Successfully renamed $playlist to $name") L.d("Successfully renamed $playlist to $name")
true true
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to rename $playlist to $name: $e") L.e("Unable to rename $playlist to $name: $e")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl } synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl }
false false
} }
@ -230,11 +230,11 @@ private class UserLibraryImpl(
return try { return try {
playlistDao.deletePlaylist(playlist.uid) playlistDao.deletePlaylist(playlist.uid)
T.d("Successfully deleted $playlist") L.d("Successfully deleted $playlist")
true true
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to delete $playlist: $e") L.e("Unable to delete $playlist: $e")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl } synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl }
false false
} }
@ -249,11 +249,11 @@ private class UserLibraryImpl(
return try { return try {
playlistDao.insertPlaylistSongs(playlist.uid, songs.map { PlaylistSong(it.uid) }) playlistDao.insertPlaylistSongs(playlist.uid, songs.map { PlaylistSong(it.uid) })
T.d("Successfully added ${songs.size} songs to $playlist") L.d("Successfully added ${songs.size} songs to $playlist")
true true
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to add ${songs.size} songs to $playlist: $e") L.e("Unable to add ${songs.size} songs to $playlist: $e")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl } synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl }
false false
} }
@ -268,11 +268,11 @@ private class UserLibraryImpl(
return try { return try {
playlistDao.replacePlaylistSongs(playlist.uid, songs.map { PlaylistSong(it.uid) }) playlistDao.replacePlaylistSongs(playlist.uid, songs.map { PlaylistSong(it.uid) })
T.d("Successfully rewrote $playlist with ${songs.size} songs") L.d("Successfully rewrote $playlist with ${songs.size} songs")
true true
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to rewrite $playlist with ${songs.size} songs: $e") L.e("Unable to rewrite $playlist with ${songs.size} songs: $e")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl } synchronized(this) { playlistMap[playlistImpl.uid] = playlistImpl }
false false
} }

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.ui.ViewBindingFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.getColorCompat import org.oxycblt.auxio.util.getColorCompat
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingFragment] that shows the current playback state in a compact manner. * A [ViewBindingFragment] that shows the current playback state in a compact manner.
@ -128,7 +128,7 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
val binding = requireBinding() val binding = requireBinding()
when (actionMode) { when (actionMode) {
ActionMode.NEXT -> { ActionMode.NEXT -> {
T.d("Using skip next action") L.d("Using skip next action")
binding.playbackSecondaryAction.apply { binding.playbackSecondaryAction.apply {
if (tag != actionMode) { if (tag != actionMode) {
setIconResource(R.drawable.ic_skip_next_24) setIconResource(R.drawable.ic_skip_next_24)
@ -140,7 +140,7 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
} }
} }
ActionMode.REPEAT -> { ActionMode.REPEAT -> {
T.d("Using repeat mode action") L.d("Using repeat mode action")
binding.playbackSecondaryAction.apply { binding.playbackSecondaryAction.apply {
if (tag != actionMode) { if (tag != actionMode) {
contentDescription = getString(R.string.desc_change_repeat) contentDescription = getString(R.string.desc_change_repeat)
@ -153,7 +153,7 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
} }
} }
ActionMode.SHUFFLE -> { ActionMode.SHUFFLE -> {
T.d("Using shuffle action") L.d("Using shuffle action")
binding.playbackSecondaryAction.apply { binding.playbackSecondaryAction.apply {
if (tag != actionMode) { if (tag != actionMode) {
setIconResource(R.drawable.sel_shuffle_state_24) setIconResource(R.drawable.sel_shuffle_state_24)

View file

@ -45,7 +45,7 @@ import org.oxycblt.auxio.ui.ViewBindingFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.systemBarInsetsCompat import org.oxycblt.auxio.util.systemBarInsetsCompat
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingFragment] more information about the currently playing song, alongside all * A [ViewBindingFragment] more information about the currently playing song, alongside all
@ -179,7 +179,7 @@ class PlaybackPanelFragment :
override fun onMenuItemClick(item: MenuItem): Boolean { override fun onMenuItemClick(item: MenuItem): Boolean {
if (item.itemId == R.id.action_open_equalizer) { if (item.itemId == R.id.action_open_equalizer) {
// Launch the system equalizer app, if possible. // Launch the system equalizer app, if possible.
T.d("Launching equalizer") L.d("Launching equalizer")
val equalizerIntent = val equalizerIntent =
Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL) Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL)
// Provide audio session ID so the equalizer can show options for this app // Provide audio session ID so the equalizer can show options for this app
@ -220,7 +220,7 @@ class PlaybackPanelFragment :
val binding = requireBinding() val binding = requireBinding()
val context = requireContext() val context = requireContext()
T.d("Updating song display: $song") L.d("Updating song display: $song")
binding.playbackCover.bind(song) binding.playbackCover.bind(song)
binding.playbackSong.text = song.name.resolve(context) binding.playbackSong.text = song.name.resolve(context)
binding.playbackArtist.text = song.artists.resolveNames(context) binding.playbackArtist.text = song.artists.resolveNames(context)

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.playback.replaygain.ReplayGainMode import org.oxycblt.auxio.playback.replaygain.ReplayGainMode
import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* User configuration specific to the playback system. * User configuration specific to the playback system.
@ -146,7 +146,7 @@ class PlaybackSettingsImpl @Inject constructor(@ApplicationContext context: Cont
} }
if (sharedPreferences.contains(OLD_KEY_LIB_MUSIC_PLAYBACK_MODE)) { if (sharedPreferences.contains(OLD_KEY_LIB_MUSIC_PLAYBACK_MODE)) {
T.d("Migrating $OLD_KEY_LIB_MUSIC_PLAYBACK_MODE") L.d("Migrating $OLD_KEY_LIB_MUSIC_PLAYBACK_MODE")
val mode = val mode =
sharedPreferences sharedPreferences
@ -162,7 +162,7 @@ class PlaybackSettingsImpl @Inject constructor(@ApplicationContext context: Cont
} }
if (sharedPreferences.contains(OLD_KEY_DETAIL_MUSIC_PLAYBACK_MODE)) { if (sharedPreferences.contains(OLD_KEY_DETAIL_MUSIC_PLAYBACK_MODE)) {
T.d("Migrating $OLD_KEY_DETAIL_MUSIC_PLAYBACK_MODE") L.d("Migrating $OLD_KEY_DETAIL_MUSIC_PLAYBACK_MODE")
val mode = val mode =
sharedPreferences sharedPreferences
@ -183,19 +183,19 @@ class PlaybackSettingsImpl @Inject constructor(@ApplicationContext context: Cont
getString(R.string.set_key_replay_gain), getString(R.string.set_key_replay_gain),
getString(R.string.set_key_pre_amp_with), getString(R.string.set_key_pre_amp_with),
getString(R.string.set_key_pre_amp_without) -> { getString(R.string.set_key_pre_amp_without) -> {
T.d("Dispatching ReplayGain setting change") L.d("Dispatching ReplayGain setting change")
listener.onReplayGainSettingsChanged() listener.onReplayGainSettingsChanged()
} }
getString(R.string.set_key_notif_action) -> { getString(R.string.set_key_notif_action) -> {
T.d("Dispatching notification setting change") L.d("Dispatching notification setting change")
listener.onNotificationActionChanged() listener.onNotificationActionChanged()
} }
getString(R.string.set_key_bar_action) -> { getString(R.string.set_key_bar_action) -> {
T.d("Dispatching bar action change") L.d("Dispatching bar action change")
listener.onBarActionChanged() listener.onBarActionChanged()
} }
getString(R.string.set_key_repeat_pause) -> { getString(R.string.set_key_repeat_pause) -> {
T.d("Dispatching pause on repeat change") L.d("Dispatching pause on repeat change")
listener.onPauseOnRepeatChanged() listener.onPauseOnRepeatChanged()
} }
} }

View file

@ -43,7 +43,7 @@ import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.state.ShuffleMode import org.oxycblt.auxio.playback.state.ShuffleMode
import org.oxycblt.auxio.util.Event import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent import org.oxycblt.auxio.util.MutableEvent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* An [ViewModel] that provides a safe UI frontend for the current playback state. * An [ViewModel] that provides a safe UI frontend for the current playback state.
@ -129,20 +129,20 @@ constructor(
} }
override fun onIndexMoved(index: Int) { override fun onIndexMoved(index: Int) {
T.d("Index moved, updating current song") L.d("Index moved, updating current song")
_song.value = playbackManager.currentSong _song.value = playbackManager.currentSong
} }
override fun onQueueChanged(queue: List<Song>, index: Int, change: QueueChange) { override fun onQueueChanged(queue: List<Song>, index: Int, change: QueueChange) {
// Other types of queue changes preserve the current song. // Other types of queue changes preserve the current song.
if (change.type == QueueChange.Type.SONG) { if (change.type == QueueChange.Type.SONG) {
T.d("Queue changed, updating current song") L.d("Queue changed, updating current song")
_song.value = playbackManager.currentSong _song.value = playbackManager.currentSong
} }
} }
override fun onQueueReordered(queue: List<Song>, index: Int, isShuffled: Boolean) { override fun onQueueReordered(queue: List<Song>, index: Int, isShuffled: Boolean) {
T.d("Queue completely changed, updating current song") L.d("Queue completely changed, updating current song")
_isShuffled.value = isShuffled _isShuffled.value = isShuffled
} }
@ -152,14 +152,14 @@ constructor(
index: Int, index: Int,
isShuffled: Boolean isShuffled: Boolean
) { ) {
T.d("New playback started, updating playback information") L.d("New playback started, updating playback information")
_song.value = playbackManager.currentSong _song.value = playbackManager.currentSong
_parent.value = parent _parent.value = parent
_isShuffled.value = isShuffled _isShuffled.value = isShuffled
} }
override fun onProgressionChanged(progression: Progression) { override fun onProgressionChanged(progression: Progression) {
T.d("Player state changed, starting new position polling") L.d("Player state changed, starting new position polling")
_isPlaying.value = progression.isPlaying _isPlaying.value = progression.isPlaying
// Still need to update the position now due to co-routine launch delays // Still need to update the position now due to co-routine launch delays
_positionDs.value = progression.calculateElapsedPositionMs().msToDs() _positionDs.value = progression.calculateElapsedPositionMs().msToDs()
@ -187,7 +187,7 @@ constructor(
// --- PLAYING FUNCTIONS --- // --- PLAYING FUNCTIONS ---
fun play(song: Song, with: PlaySong) { fun play(song: Song, with: PlaySong) {
T.d("Playing $song with $with") L.d("Playing $song with $with")
playWithImpl(song, with, ShuffleMode.IMPLICIT) playWithImpl(song, with, ShuffleMode.IMPLICIT)
} }
@ -201,7 +201,7 @@ constructor(
/** Shuffle all songs in the music library. */ /** Shuffle all songs in the music library. */
fun shuffleAll() { fun shuffleAll() {
T.d("Shuffling all songs") L.d("Shuffling all songs")
playFromAllImpl(null, ShuffleMode.ON) playFromAllImpl(null, ShuffleMode.ON)
} }
@ -257,7 +257,7 @@ constructor(
} }
private fun playFromAlbumImpl(song: Song, shuffle: ShuffleMode) { private fun playFromAlbumImpl(song: Song, shuffle: ShuffleMode) {
T.d("Playing $song from album") L.d("Playing $song from album")
playImpl(commandFactory.songFromAlbum(song, shuffle)) playImpl(commandFactory.songFromAlbum(song, shuffle))
} }
@ -267,7 +267,7 @@ constructor(
playbackManager.play(params) playbackManager.play(params)
return return
} }
T.d( L.d(
"Cannot use given artist parameter for $song [$artist from ${song.artists}], showing choice dialog") "Cannot use given artist parameter for $song [$artist from ${song.artists}], showing choice dialog")
startPlaybackDecision(PlaybackDecision.PlayFromArtist(song)) startPlaybackDecision(PlaybackDecision.PlayFromArtist(song))
} }
@ -278,20 +278,20 @@ constructor(
playbackManager.play(params) playbackManager.play(params)
return return
} }
T.d( L.d(
"Cannot use given genre parameter for $song [$genre from ${song.genres}], showing choice dialog") "Cannot use given genre parameter for $song [$genre from ${song.genres}], showing choice dialog")
startPlaybackDecision(PlaybackDecision.PlayFromArtist(song)) startPlaybackDecision(PlaybackDecision.PlayFromArtist(song))
} }
private fun playFromPlaylistImpl(song: Song, playlist: Playlist, shuffle: ShuffleMode) { private fun playFromPlaylistImpl(song: Song, playlist: Playlist, shuffle: ShuffleMode) {
T.d("Playing $song from $playlist") L.d("Playing $song from $playlist")
playImpl(commandFactory.songFromPlaylist(song, playlist, shuffle)) playImpl(commandFactory.songFromPlaylist(song, playlist, shuffle))
} }
private fun startPlaybackDecision(decision: PlaybackDecision) { private fun startPlaybackDecision(decision: PlaybackDecision) {
val existing = _playbackDecision.flow.value val existing = _playbackDecision.flow.value
if (existing != null) { if (existing != null) {
T.d("Already handling decision $existing, ignoring $decision") L.d("Already handling decision $existing, ignoring $decision")
return return
} }
_playbackDecision.put(decision) _playbackDecision.put(decision)
@ -303,7 +303,7 @@ constructor(
* @param album The [Album] to play. * @param album The [Album] to play.
*/ */
fun play(album: Album) { fun play(album: Album) {
T.d("Playing $album") L.d("Playing $album")
playImpl(commandFactory.album(album, ShuffleMode.OFF)) playImpl(commandFactory.album(album, ShuffleMode.OFF))
} }
@ -313,7 +313,7 @@ constructor(
* @param album The [Album] to shuffle. * @param album The [Album] to shuffle.
*/ */
fun shuffle(album: Album) { fun shuffle(album: Album) {
T.d("Shuffling $album") L.d("Shuffling $album")
playImpl(commandFactory.album(album, ShuffleMode.ON)) playImpl(commandFactory.album(album, ShuffleMode.ON))
} }
@ -323,7 +323,7 @@ constructor(
* @param artist The [Artist] to play. * @param artist The [Artist] to play.
*/ */
fun play(artist: Artist) { fun play(artist: Artist) {
T.d("Playing $artist") L.d("Playing $artist")
playImpl(commandFactory.artist(artist, ShuffleMode.OFF)) playImpl(commandFactory.artist(artist, ShuffleMode.OFF))
} }
@ -333,7 +333,7 @@ constructor(
* @param artist The [Artist] to shuffle. * @param artist The [Artist] to shuffle.
*/ */
fun shuffle(artist: Artist) { fun shuffle(artist: Artist) {
T.d("Shuffling $artist") L.d("Shuffling $artist")
playImpl(commandFactory.artist(artist, ShuffleMode.ON)) playImpl(commandFactory.artist(artist, ShuffleMode.ON))
} }
@ -343,7 +343,7 @@ constructor(
* @param genre The [Genre] to play. * @param genre The [Genre] to play.
*/ */
fun play(genre: Genre) { fun play(genre: Genre) {
T.d("Playing $genre") L.d("Playing $genre")
playImpl(commandFactory.genre(genre, ShuffleMode.OFF)) playImpl(commandFactory.genre(genre, ShuffleMode.OFF))
} }
@ -353,7 +353,7 @@ constructor(
* @param genre The [Genre] to shuffle. * @param genre The [Genre] to shuffle.
*/ */
fun shuffle(genre: Genre) { fun shuffle(genre: Genre) {
T.d("Shuffling $genre") L.d("Shuffling $genre")
playImpl(commandFactory.genre(genre, ShuffleMode.ON)) playImpl(commandFactory.genre(genre, ShuffleMode.ON))
} }
@ -363,7 +363,7 @@ constructor(
* @param playlist The [Playlist] to play. * @param playlist The [Playlist] to play.
*/ */
fun play(playlist: Playlist) { fun play(playlist: Playlist) {
T.d("Playing $playlist") L.d("Playing $playlist")
playImpl(commandFactory.playlist(playlist, ShuffleMode.OFF)) playImpl(commandFactory.playlist(playlist, ShuffleMode.OFF))
} }
@ -373,7 +373,7 @@ constructor(
* @param playlist The [Playlist] to shuffle. * @param playlist The [Playlist] to shuffle.
*/ */
fun shuffle(playlist: Playlist) { fun shuffle(playlist: Playlist) {
T.d("Shuffling $playlist") L.d("Shuffling $playlist")
playImpl(commandFactory.playlist(playlist, ShuffleMode.ON)) playImpl(commandFactory.playlist(playlist, ShuffleMode.ON))
} }
@ -383,7 +383,7 @@ constructor(
* @param songs The [Song]s to play. * @param songs The [Song]s to play.
*/ */
fun play(songs: List<Song>) { fun play(songs: List<Song>) {
T.d("Playing ${songs.size} songs") L.d("Playing ${songs.size} songs")
playImpl(commandFactory.songs(songs, ShuffleMode.OFF)) playImpl(commandFactory.songs(songs, ShuffleMode.OFF))
} }
@ -393,7 +393,7 @@ constructor(
* @param songs The [Song]s to shuffle. * @param songs The [Song]s to shuffle.
*/ */
fun shuffle(songs: List<Song>) { fun shuffle(songs: List<Song>) {
T.d("Shuffling ${songs.size} songs") L.d("Shuffling ${songs.size} songs")
playImpl(commandFactory.songs(songs, ShuffleMode.ON)) playImpl(commandFactory.songs(songs, ShuffleMode.ON))
} }
@ -408,7 +408,7 @@ constructor(
* @param action The [DeferredPlayback] to perform eventually. * @param action The [DeferredPlayback] to perform eventually.
*/ */
fun playDeferred(action: DeferredPlayback) { fun playDeferred(action: DeferredPlayback) {
T.d("Starting action $action") L.d("Starting action $action")
playbackManager.playDeferred(action) playbackManager.playDeferred(action)
} }
@ -420,7 +420,7 @@ constructor(
* @param positionDs The position to seek to, in deci-seconds (1/10th of a second). * @param positionDs The position to seek to, in deci-seconds (1/10th of a second).
*/ */
fun seekTo(positionDs: Long) { fun seekTo(positionDs: Long) {
T.d("Seeking to ${positionDs}ds") L.d("Seeking to ${positionDs}ds")
playbackManager.seekTo(positionDs.dsToMs()) playbackManager.seekTo(positionDs.dsToMs())
} }
@ -428,13 +428,13 @@ constructor(
/** Skip to the next [Song]. */ /** Skip to the next [Song]. */
fun next() { fun next() {
T.d("Skipping to next song") L.d("Skipping to next song")
playbackManager.next() playbackManager.next()
} }
/** Skip to the previous [Song]. */ /** Skip to the previous [Song]. */
fun prev() { fun prev() {
T.d("Skipping to previous song") L.d("Skipping to previous song")
playbackManager.prev() playbackManager.prev()
} }
@ -444,7 +444,7 @@ constructor(
* @param song The [Song] to add. * @param song The [Song] to add.
*/ */
fun playNext(song: Song) { fun playNext(song: Song) {
T.d("Playing $song next") L.d("Playing $song next")
playbackManager.playNext(song) playbackManager.playNext(song)
} }
@ -454,7 +454,7 @@ constructor(
* @param album The [Album] to add. * @param album The [Album] to add.
*/ */
fun playNext(album: Album) { fun playNext(album: Album) {
T.d("Playing $album next") L.d("Playing $album next")
playbackManager.playNext(listSettings.albumSongSort.songs(album.songs)) playbackManager.playNext(listSettings.albumSongSort.songs(album.songs))
} }
@ -464,7 +464,7 @@ constructor(
* @param artist The [Artist] to add. * @param artist The [Artist] to add.
*/ */
fun playNext(artist: Artist) { fun playNext(artist: Artist) {
T.d("Playing $artist next") L.d("Playing $artist next")
playbackManager.playNext(listSettings.artistSongSort.songs(artist.songs)) playbackManager.playNext(listSettings.artistSongSort.songs(artist.songs))
} }
@ -474,7 +474,7 @@ constructor(
* @param genre The [Genre] to add. * @param genre The [Genre] to add.
*/ */
fun playNext(genre: Genre) { fun playNext(genre: Genre) {
T.d("Playing $genre next") L.d("Playing $genre next")
playbackManager.playNext(listSettings.genreSongSort.songs(genre.songs)) playbackManager.playNext(listSettings.genreSongSort.songs(genre.songs))
} }
@ -484,7 +484,7 @@ constructor(
* @param playlist The [Playlist] to add. * @param playlist The [Playlist] to add.
*/ */
fun playNext(playlist: Playlist) { fun playNext(playlist: Playlist) {
T.d("Playing $playlist next") L.d("Playing $playlist next")
playbackManager.playNext(playlist.songs) playbackManager.playNext(playlist.songs)
} }
@ -494,7 +494,7 @@ constructor(
* @param songs The [Song]s to add. * @param songs The [Song]s to add.
*/ */
fun playNext(songs: List<Song>) { fun playNext(songs: List<Song>) {
T.d("Playing ${songs.size} songs next") L.d("Playing ${songs.size} songs next")
playbackManager.playNext(songs) playbackManager.playNext(songs)
} }
@ -504,7 +504,7 @@ constructor(
* @param song The [Song] to add. * @param song The [Song] to add.
*/ */
fun addToQueue(song: Song) { fun addToQueue(song: Song) {
T.d("Adding $song to queue") L.d("Adding $song to queue")
playbackManager.addToQueue(song) playbackManager.addToQueue(song)
} }
@ -514,7 +514,7 @@ constructor(
* @param album The [Album] to add. * @param album The [Album] to add.
*/ */
fun addToQueue(album: Album) { fun addToQueue(album: Album) {
T.d("Adding $album to queue") L.d("Adding $album to queue")
playbackManager.addToQueue(listSettings.albumSongSort.songs(album.songs)) playbackManager.addToQueue(listSettings.albumSongSort.songs(album.songs))
} }
@ -524,7 +524,7 @@ constructor(
* @param artist The [Artist] to add. * @param artist The [Artist] to add.
*/ */
fun addToQueue(artist: Artist) { fun addToQueue(artist: Artist) {
T.d("Adding $artist to queue") L.d("Adding $artist to queue")
playbackManager.addToQueue(listSettings.artistSongSort.songs(artist.songs)) playbackManager.addToQueue(listSettings.artistSongSort.songs(artist.songs))
} }
@ -534,7 +534,7 @@ constructor(
* @param genre The [Genre] to add. * @param genre The [Genre] to add.
*/ */
fun addToQueue(genre: Genre) { fun addToQueue(genre: Genre) {
T.d("Adding $genre to queue") L.d("Adding $genre to queue")
playbackManager.addToQueue(listSettings.genreSongSort.songs(genre.songs)) playbackManager.addToQueue(listSettings.genreSongSort.songs(genre.songs))
} }
@ -544,7 +544,7 @@ constructor(
* @param playlist The [Playlist] to add. * @param playlist The [Playlist] to add.
*/ */
fun addToQueue(playlist: Playlist) { fun addToQueue(playlist: Playlist) {
T.d("Adding $playlist to queue") L.d("Adding $playlist to queue")
playbackManager.addToQueue(playlist.songs) playbackManager.addToQueue(playlist.songs)
} }
@ -554,7 +554,7 @@ constructor(
* @param songs The [Song]s to add. * @param songs The [Song]s to add.
*/ */
fun addToQueue(songs: List<Song>) { fun addToQueue(songs: List<Song>) {
T.d("Adding ${songs.size} songs to queue") L.d("Adding ${songs.size} songs to queue")
playbackManager.addToQueue(songs) playbackManager.addToQueue(songs)
} }
@ -562,13 +562,13 @@ constructor(
/** Toggle [isPlaying] (i.e from playing to paused) */ /** Toggle [isPlaying] (i.e from playing to paused) */
fun togglePlaying() { fun togglePlaying() {
T.d("Toggling playing state") L.d("Toggling playing state")
playbackManager.playing(!playbackManager.progression.isPlaying) playbackManager.playing(!playbackManager.progression.isPlaying)
} }
/** Toggle [isShuffled] (ex. from on to off) */ /** Toggle [isShuffled] (ex. from on to off) */
fun toggleShuffled() { fun toggleShuffled() {
T.d("Toggling shuffled state") L.d("Toggling shuffled state")
playbackManager.shuffled(!playbackManager.isShuffled) playbackManager.shuffled(!playbackManager.isShuffled)
} }
@ -578,7 +578,7 @@ constructor(
* @see RepeatMode.increment * @see RepeatMode.increment
*/ */
fun toggleRepeatMode() { fun toggleRepeatMode() {
T.d("Toggling repeat mode") L.d("Toggling repeat mode")
playbackManager.repeatMode(playbackManager.repeatMode.increment()) playbackManager.repeatMode(playbackManager.repeatMode.increment())
} }
@ -599,7 +599,7 @@ constructor(
private fun openImpl(panel: OpenPanel) { private fun openImpl(panel: OpenPanel) {
val existing = openPanel.flow.value val existing = openPanel.flow.value
if (existing != null) { if (existing != null) {
T.d("Already opening $existing, ignoring opening $panel") L.d("Already opening $existing, ignoring opening $panel")
return return
} }
_openPanel.put(panel) _openPanel.put(panel)

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A picker [ViewBindingMaterialDialogFragment] intended for when [Artist] playback is ambiguous. * A picker [ViewBindingMaterialDialogFragment] intended for when [Artist] playback is ambiguous.
@ -88,7 +88,7 @@ class PlayFromArtistDialog :
private fun updateSong(song: Song?) { private fun updateSong(song: Song?) {
if (song == null) { if (song == null) {
T.d("No song to show choices for, navigating away") L.d("No song to show choices for, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A picker [ViewBindingMaterialDialogFragment] intended for when [Genre] playback is ambiguous. * A picker [ViewBindingMaterialDialogFragment] intended for when [Genre] playback is ambiguous.
@ -88,7 +88,7 @@ class PlayFromGenreDialog :
private fun updateSong(song: Song?) { private fun updateSong(song: Song?) {
if (song == null) { if (song == null) {
T.d("No song to show choices for, navigating away") L.d("No song to show choices for, navigating away")
findNavController().navigateUp() findNavController().navigateUp()
return return
} }

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewModel] that stores the choices shown in the playback picker dialogs. * A [ViewModel] that stores the choices shown in the playback picker dialogs.
@ -63,10 +63,10 @@ class PlaybackPickerViewModel @Inject constructor(private val musicRepository: M
* @param uid The [Music.UID] of the item to show. Must be a [Song]. * @param uid The [Music.UID] of the item to show. Must be a [Song].
*/ */
fun setPickerSongUid(uid: Music.UID) { fun setPickerSongUid(uid: Music.UID) {
T.d("Opening picker for song $uid") L.d("Opening picker for song $uid")
_currentPickerSong.value = musicRepository.deviceLibrary?.findSong(uid) _currentPickerSong.value = musicRepository.deviceLibrary?.findSong(uid)
if (_currentPickerSong.value != null) { if (_currentPickerSong.value != null) {
T.w("Given song UID was invalid") L.w("Given song UID was invalid")
} }
} }
} }

View file

@ -22,7 +22,7 @@ import javax.inject.Inject
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Manages the persisted playback state in a structured manner. * Manages the persisted playback state in a structured manner.
@ -59,8 +59,8 @@ constructor(
heapItems = queueDao.getHeap() heapItems = queueDao.getHeap()
mappingItems = queueDao.getShuffledMapping() mappingItems = queueDao.getShuffledMapping()
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable read playback state") L.e("Unable read playback state")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
return null return null
} }
@ -84,12 +84,12 @@ constructor(
queueDao.nukeHeap() queueDao.nukeHeap()
queueDao.nukeShuffledMapping() queueDao.nukeShuffledMapping()
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to clear previous state") L.e("Unable to clear previous state")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
return false return false
} }
T.d("Successfully cleared previous state") L.d("Successfully cleared previous state")
if (state != null) { if (state != null) {
// Transform saved state into raw state, which can then be written to the database. // Transform saved state into raw state, which can then be written to the database.
val playbackState = val playbackState =
@ -113,12 +113,12 @@ constructor(
queueDao.insertHeap(heap) queueDao.insertHeap(heap)
queueDao.insertShuffledMapping(shuffledMapping) queueDao.insertShuffledMapping(shuffledMapping)
} catch (e: Exception) { } catch (e: Exception) {
T.e("Unable to write new state") L.e("Unable to write new state")
T.e(e.stackTraceToString()) L.e(e.stackTraceToString())
return false return false
} }
T.d("Successfully wrote new state") L.d("Successfully wrote new state")
} }
return true return true

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.util.context import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.inflater
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [RecyclerView.Adapter] that shows an editable list of queue items. * A [RecyclerView.Adapter] that shows an editable list of queue items.
@ -80,7 +80,7 @@ class QueueAdapter(private val listener: EditClickListListener<Song>) :
* @param isPlaying Whether playback is ongoing or paused. * @param isPlaying Whether playback is ongoing or paused.
*/ */
fun setPosition(index: Int, isPlaying: Boolean) { fun setPosition(index: Int, isPlaying: Boolean) {
T.d("Updating index") L.d("Updating index")
val lastIndex = currentIndex val lastIndex = currentIndex
currentIndex = index currentIndex = index
@ -89,10 +89,10 @@ class QueueAdapter(private val listener: EditClickListListener<Song>) :
// TODO: Optimize this by only updating the range between old and new indices? // TODO: Optimize this by only updating the range between old and new indices?
// TODO: Don't update when the index has not moved. // TODO: Don't update when the index has not moved.
if (currentIndex < lastIndex) { if (currentIndex < lastIndex) {
T.d("Moved backwards, must update items above last index") L.d("Moved backwards, must update items above last index")
notifyItemRangeChanged(0, lastIndex + 1, PAYLOAD_UPDATE_POSITION) notifyItemRangeChanged(0, lastIndex + 1, PAYLOAD_UPDATE_POSITION)
} else { } else {
T.d("Moved forwards, update items after index") L.d("Moved forwards, update items after index")
notifyItemRangeChanged(0, currentIndex + 1, PAYLOAD_UPDATE_POSITION) notifyItemRangeChanged(0, currentIndex + 1, PAYLOAD_UPDATE_POSITION)
} }

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingFragment import org.oxycblt.auxio.ui.ViewBindingFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewBindingFragment] that displays an editable queue. * A [ViewBindingFragment] that displays an editable queue.
@ -122,14 +122,14 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), EditClickList
// dependent on where we have to scroll to get to the currently playing song. // dependent on where we have to scroll to get to the currently playing song.
if (notInitialized || scrollTo < start) { if (notInitialized || scrollTo < start) {
// We need to scroll upwards, or initialize the scroll, no need to offset // We need to scroll upwards, or initialize the scroll, no need to offset
T.d("Not scrolling downwards, no offset needed") L.d("Not scrolling downwards, no offset needed")
binding.queueRecycler.scrollToPosition(scrollTo) binding.queueRecycler.scrollToPosition(scrollTo)
} else if (scrollTo > end) { } else if (scrollTo > end) {
// We need to scroll downwards, we need to offset by a screen of songs. // We need to scroll downwards, we need to offset by a screen of songs.
// This does have some error due to how many completely visible items on-screen // This does have some error due to how many completely visible items on-screen
// can vary. This is considered okay. // can vary. This is considered okay.
val offset = scrollTo + (end - start) val offset = scrollTo + (end - start)
T.d("Scrolling downwards, offsetting by $offset") L.d("Scrolling downwards, offsetting by $offset")
binding.queueRecycler.scrollToPosition(min(queue.lastIndex, offset)) binding.queueRecycler.scrollToPosition(min(queue.lastIndex, offset))
} }
} }

View file

@ -30,7 +30,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.playback.state.QueueChange import org.oxycblt.auxio.playback.state.QueueChange
import org.oxycblt.auxio.util.Event import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent import org.oxycblt.auxio.util.MutableEvent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [ViewModel] that manages the current queue state and allows navigation through the queue. * A [ViewModel] that manages the current queue state and allows navigation through the queue.
@ -62,26 +62,26 @@ class QueueViewModel @Inject constructor(private val playbackManager: PlaybackSt
} }
override fun onIndexMoved(index: Int) { override fun onIndexMoved(index: Int) {
T.d("Index moved, synchronizing and scrolling to new position") L.d("Index moved, synchronizing and scrolling to new position")
_scrollTo.put(index) _scrollTo.put(index)
_index.value = index _index.value = index
} }
override fun onQueueChanged(queue: List<Song>, index: Int, change: QueueChange) { override fun onQueueChanged(queue: List<Song>, index: Int, change: QueueChange) {
// Queue changed trivially due to item mo -> Diff queue, stay at current index. // Queue changed trivially due to item mo -> Diff queue, stay at current index.
T.d("Updating queue display") L.d("Updating queue display")
_queueInstructions.put(change.instructions) _queueInstructions.put(change.instructions)
_queue.value = queue _queue.value = queue
if (change.type != QueueChange.Type.MAPPING) { if (change.type != QueueChange.Type.MAPPING) {
// Index changed, make sure it remains updated without actually scrolling to it. // Index changed, make sure it remains updated without actually scrolling to it.
T.d("Index changed with queue, synchronizing new position") L.d("Index changed with queue, synchronizing new position")
_index.value = index _index.value = index
} }
} }
override fun onQueueReordered(queue: List<Song>, index: Int, isShuffled: Boolean) { override fun onQueueReordered(queue: List<Song>, index: Int, isShuffled: Boolean) {
// Queue changed completely -> Replace queue, update index // Queue changed completely -> Replace queue, update index
T.d("Queue changed completely, replacing queue and position") L.d("Queue changed completely, replacing queue and position")
_queueInstructions.put(UpdateInstructions.Replace(0)) _queueInstructions.put(UpdateInstructions.Replace(0))
_scrollTo.put(index) _scrollTo.put(index)
_queue.value = queue _queue.value = queue
@ -95,7 +95,7 @@ class QueueViewModel @Inject constructor(private val playbackManager: PlaybackSt
isShuffled: Boolean isShuffled: Boolean
) { ) {
// Entirely new queue -> Replace queue, update index // Entirely new queue -> Replace queue, update index
T.d("New playback, replacing queue and position") L.d("New playback, replacing queue and position")
_queueInstructions.put(UpdateInstructions.Replace(0)) _queueInstructions.put(UpdateInstructions.Replace(0))
_scrollTo.put(index) _scrollTo.put(index)
_queue.value = queue _queue.value = queue
@ -117,7 +117,7 @@ class QueueViewModel @Inject constructor(private val playbackManager: PlaybackSt
if (adapterIndex !in queue.value.indices) { if (adapterIndex !in queue.value.indices) {
return return
} }
T.d("Going to position $adapterIndex in queue") L.d("Going to position $adapterIndex in queue")
playbackManager.goto(adapterIndex) playbackManager.goto(adapterIndex)
} }
@ -131,7 +131,7 @@ class QueueViewModel @Inject constructor(private val playbackManager: PlaybackSt
if (adapterIndex !in queue.value.indices) { if (adapterIndex !in queue.value.indices) {
return return
} }
T.d("Removing item $adapterIndex in queue") L.d("Removing item $adapterIndex in queue")
playbackManager.removeQueueItem(adapterIndex) playbackManager.removeQueueItem(adapterIndex)
} }
@ -146,7 +146,7 @@ class QueueViewModel @Inject constructor(private val playbackManager: PlaybackSt
if (adapterFrom !in queue.value.indices || adapterTo !in queue.value.indices) { if (adapterFrom !in queue.value.indices || adapterTo !in queue.value.indices) {
return false return false
} }
T.d("Moving $adapterFrom to $adapterFrom in queue") L.d("Moving $adapterFrom to $adapterFrom in queue")
playbackManager.moveQueueItem(adapterFrom, adapterTo) playbackManager.moveQueueItem(adapterFrom, adapterTo)
return true return true
} }

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogPreAmpBinding import org.oxycblt.auxio.databinding.DialogPreAmpBinding
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* aa [ViewBindingMaterialDialogFragment] that allows user configuration of the current * aa [ViewBindingMaterialDialogFragment] that allows user configuration of the current
@ -62,7 +62,7 @@ class PreAmpCustomizeDialog : ViewBindingMaterialDialogFragment<DialogPreAmpBind
// settings. After this, the sliders save their own state, so we do not need to // settings. After this, the sliders save their own state, so we do not need to
// do any restore behavior. // do any restore behavior.
val preAmp = playbackSettings.replayGainPreAmp val preAmp = playbackSettings.replayGainPreAmp
T.d("Initializing from $preAmp") L.d("Initializing from $preAmp")
binding.withTagsSlider.value = preAmp.with binding.withTagsSlider.value = preAmp.with
binding.withoutTagsSlider.value = preAmp.without binding.withoutTagsSlider.value = preAmp.without
} }

View file

@ -32,7 +32,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.playback.state.QueueChange import org.oxycblt.auxio.playback.state.QueueChange
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* An [AudioProcessor] that handles ReplayGain values and their amplification of the audio stream. * An [AudioProcessor] that handles ReplayGain values and their amplification of the audio stream.
@ -71,7 +71,7 @@ constructor(
// --- OVERRIDES --- // --- OVERRIDES ---
override fun onIndexMoved(index: Int) { override fun onIndexMoved(index: Int) {
T.d("Index moved, updating current song") L.d("Index moved, updating current song")
applyReplayGain(playbackManager.currentSong) applyReplayGain(playbackManager.currentSong)
} }
@ -88,7 +88,7 @@ constructor(
index: Int, index: Int,
isShuffled: Boolean isShuffled: Boolean
) { ) {
T.d("New playback started, updating playback information") L.d("New playback started, updating playback information")
applyReplayGain(playbackManager.currentSong) applyReplayGain(playbackManager.currentSong)
} }
@ -106,12 +106,12 @@ constructor(
*/ */
private fun applyReplayGain(song: Song?) { private fun applyReplayGain(song: Song?) {
if (song == null) { if (song == null) {
T.d("Nothing playing, disabling adjustment") L.d("Nothing playing, disabling adjustment")
volume = 1f volume = 1f
return return
} }
T.d("Applying ReplayGain adjustment for $song") L.d("Applying ReplayGain adjustment for $song")
val gain = song.replayGainAdjustment val gain = song.replayGainAdjustment
val preAmp = playbackSettings.replayGainPreAmp val preAmp = playbackSettings.replayGainPreAmp
@ -121,44 +121,43 @@ constructor(
when (playbackSettings.replayGainMode) { when (playbackSettings.replayGainMode) {
// User wants no adjustment. // User wants no adjustment.
ReplayGainMode.OFF -> { ReplayGainMode.OFF -> {
T.d("ReplayGain is off") L.d("ReplayGain is off")
null null
} }
// User wants track gain to be preferred. Default to album gain only if // User wants track gain to be preferred. Default to album gain only if
// there is no track gain. // there is no track gain.
ReplayGainMode.TRACK -> { ReplayGainMode.TRACK -> {
T.d("Using track strategy") L.d("Using track strategy")
gain.track ?: gain.album gain.track ?: gain.album
} }
// User wants album gain to be preferred. Default to track gain only if // User wants album gain to be preferred. Default to track gain only if
// here is no album gain. // here is no album gain.
ReplayGainMode.ALBUM -> { ReplayGainMode.ALBUM -> {
T.d("Using album strategy") L.d("Using album strategy")
gain.album ?: gain.track gain.album ?: gain.track
} }
// User wants album gain to be used when in an album, track gain otherwise. // User wants album gain to be used when in an album, track gain otherwise.
ReplayGainMode.DYNAMIC -> { ReplayGainMode.DYNAMIC -> {
T.d("Using dynamic strategy") L.d("Using dynamic strategy")
gain.album?.takeIf { gain.album?.takeIf {
playbackManager.parent is Album && playbackManager.parent is Album &&
playbackManager.currentSong?.album == playbackManager.parent playbackManager.currentSong?.album == playbackManager.parent
} } ?: gain.track
?: gain.track
} }
} }
val amplifiedAdjustment = val amplifiedAdjustment =
if (resolvedAdjustment != null) { if (resolvedAdjustment != null) {
// Successfully resolved an adjustment, apply the corresponding pre-amp // Successfully resolved an adjustment, apply the corresponding pre-amp
T.d("Applying with pre-amp") L.d("Applying with pre-amp")
resolvedAdjustment + preAmp.with resolvedAdjustment + preAmp.with
} else { } else {
// No adjustment found, use the corresponding user-defined pre-amp // No adjustment found, use the corresponding user-defined pre-amp
T.d("Applying without pre-amp") L.d("Applying without pre-amp")
preAmp.without preAmp.without
} }
T.d("Applying ReplayGain adjustment ${amplifiedAdjustment}db") L.d("Applying ReplayGain adjustment ${amplifiedAdjustment}db")
// Final adjustment along the volume curve. // Final adjustment along the volume curve.
volume = 10f.pow(amplifiedAdjustment / 20f) volume = 10f.pow(amplifiedAdjustment / 20f)

View file

@ -59,7 +59,7 @@ import org.oxycblt.auxio.playback.state.RawQueue
import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.state.ShuffleMode import org.oxycblt.auxio.playback.state.ShuffleMode
import org.oxycblt.auxio.playback.state.StateAck import org.oxycblt.auxio.playback.state.StateAck
import timber.log.Timber as T import timber.log.Timber as L
class ExoPlaybackStateHolder( class ExoPlaybackStateHolder(
private val context: Context, private val context: Context,
@ -130,8 +130,8 @@ class ExoPlaybackStateHolder(
override fun resolveQueue(): RawQueue { override fun resolveQueue(): RawQueue {
val deviceLibrary = val deviceLibrary =
musicRepository.deviceLibrary musicRepository.deviceLibrary
// No library, cannot do anything. // No library, cannot do anything.
?: return RawQueue(emptyList(), emptyList(), 0) ?: return RawQueue(emptyList(), emptyList(), 0)
val heap = (0 until player.mediaItemCount).map { player.getMediaItemAt(it) } val heap = (0 until player.mediaItemCount).map { player.getMediaItemAt(it) }
val shuffledMapping = val shuffledMapping =
if (player.shuffleModeEnabled) { if (player.shuffleModeEnabled) {
@ -145,13 +145,13 @@ class ExoPlaybackStateHolder(
override fun handleDeferred(action: DeferredPlayback): Boolean { override fun handleDeferred(action: DeferredPlayback): Boolean {
val deviceLibrary = val deviceLibrary =
musicRepository.deviceLibrary musicRepository.deviceLibrary
// No library, cannot do anything. // No library, cannot do anything.
?: return false ?: return false
when (action) { when (action) {
// Restore state -> Start a new restoreState job // Restore state -> Start a new restoreState job
is DeferredPlayback.RestoreState -> { is DeferredPlayback.RestoreState -> {
T.d("Restoring playback state") L.d("Restoring playback state")
restoreScope.launch { restoreScope.launch {
val state = persistenceRepository.readState() val state = persistenceRepository.readState()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -170,7 +170,7 @@ class ExoPlaybackStateHolder(
} }
// Shuffle all -> Start new playback from all songs // Shuffle all -> Start new playback from all songs
is DeferredPlayback.ShuffleAll -> { is DeferredPlayback.ShuffleAll -> {
T.d("Shuffling all tracks") L.d("Shuffling all tracks")
playbackManager.play( playbackManager.play(
requireNotNull(commandFactory.all(ShuffleMode.ON)) { requireNotNull(commandFactory.all(ShuffleMode.ON)) {
"Invalid playback parameters" "Invalid playback parameters"
@ -178,7 +178,7 @@ class ExoPlaybackStateHolder(
} }
// Open -> Try to find the Song for the given file and then play it from all songs // Open -> Try to find the Song for the given file and then play it from all songs
is DeferredPlayback.Open -> { is DeferredPlayback.Open -> {
T.d("Opening specified file") L.d("Opening specified file")
deviceLibrary.findSongForUri(context, action.uri)?.let { song -> deviceLibrary.findSongForUri(context, action.uri)?.let { song ->
playbackManager.play( playbackManager.play(
requireNotNull(commandFactory.song(song, ShuffleMode.IMPLICIT)) { requireNotNull(commandFactory.song(song, ShuffleMode.IMPLICIT)) {
@ -427,18 +427,18 @@ class ExoPlaybackStateHolder(
if (player.playWhenReady) { if (player.playWhenReady) {
// Mark that we have started playing so that the notification can now be posted. // Mark that we have started playing so that the notification can now be posted.
T.d("Player has started playing") L.d("Player has started playing")
sessionOngoing = true sessionOngoing = true
if (!openAudioEffectSession) { if (!openAudioEffectSession) {
// Convention to start an audioeffect session on play/pause rather than // Convention to start an audioeffect session on play/pause rather than
// start/stop // start/stop
T.d("Opening audio effect session") L.d("Opening audio effect session")
broadcastAudioEffectAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) broadcastAudioEffectAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)
openAudioEffectSession = true openAudioEffectSession = true
} }
} else if (openAudioEffectSession) { } else if (openAudioEffectSession) {
// Make sure to close the audio session when we stop playback. // Make sure to close the audio session when we stop playback.
T.d("Closing audio effect session") L.d("Closing audio effect session")
broadcastAudioEffectAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION) broadcastAudioEffectAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)
openAudioEffectSession = false openAudioEffectSession = false
} }
@ -470,7 +470,7 @@ class ExoPlaybackStateHolder(
Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAY_WHEN_READY_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED, Player.EVENT_IS_PLAYING_CHANGED,
Player.EVENT_POSITION_DISCONTINUITY)) { Player.EVENT_POSITION_DISCONTINUITY)) {
T.d("Player state changed, must synchronize state") L.d("Player state changed, must synchronize state")
playbackManager.ack(this, StateAck.ProgressionChanged) playbackManager.ack(this, StateAck.ProgressionChanged)
} }
} }
@ -478,13 +478,13 @@ class ExoPlaybackStateHolder(
override fun onPlayerError(error: PlaybackException) { override fun onPlayerError(error: PlaybackException) {
// TODO: Replace with no skipping and a notification instead // TODO: Replace with no skipping and a notification instead
// If there's any issue, just go to the next song. // If there's any issue, just go to the next song.
T.e("Player error occurred") L.e("Player error occurred")
T.e(error.stackTraceToString()) L.e(error.stackTraceToString())
playbackManager.next() playbackManager.next()
} }
private fun broadcastAudioEffectAction(event: String) { private fun broadcastAudioEffectAction(event: String) {
T.d("Broadcasting AudioEffect event: $event") L.d("Broadcasting AudioEffect event: $event")
context.sendBroadcast( context.sendBroadcast(
Intent(event) Intent(event)
.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName) .putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName)
@ -497,7 +497,7 @@ class ExoPlaybackStateHolder(
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
if (changes.deviceLibrary && musicRepository.deviceLibrary != null) { if (changes.deviceLibrary && musicRepository.deviceLibrary != null) {
// We now have a library, see if we have anything we need to do. // We now have a library, see if we have anything we need to do.
T.d("Library obtained, requesting action") L.d("Library obtained, requesting action")
playbackManager.requestAction(this) playbackManager.requestAction(this)
} }
} }
@ -523,17 +523,17 @@ class ExoPlaybackStateHolder(
private fun deferSave() { private fun deferSave() {
saveJob { saveJob {
T.d("Waiting for save buffer") L.d("Waiting for save buffer")
delay(SAVE_BUFFER) delay(SAVE_BUFFER)
yield() yield()
T.d("Committing saved state") L.d("Committing saved state")
persistenceRepository.saveState(playbackManager.toSavedState()) persistenceRepository.saveState(playbackManager.toSavedState())
} }
} }
private fun saveJob(block: suspend () -> Unit) { private fun saveJob(block: suspend () -> Unit) {
currentSaveJob?.let { currentSaveJob?.let {
T.d("Discarding prior save job") L.d("Discarding prior save job")
it.cancel() it.cancel()
} }
currentSaveJob = saveScope.launch { block() } currentSaveJob = saveScope.launch { block() }

View file

@ -27,7 +27,7 @@ import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.AuxioService import org.oxycblt.auxio.AuxioService
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [BroadcastReceiver] that forwards [Intent.ACTION_MEDIA_BUTTON] [Intent]s to * A [BroadcastReceiver] that forwards [Intent.ACTION_MEDIA_BUTTON] [Intent]s to
@ -47,7 +47,7 @@ class MediaButtonReceiver : BroadcastReceiver() {
// stupid this is with the state of foreground services on modern android. One // stupid this is with the state of foreground services on modern android. One
// wrong action at the wrong time will result in the app crashing, and there is // wrong action at the wrong time will result in the app crashing, and there is
// nothing I can do about it. // nothing I can do about it.
T.d("Delivering media button intent $intent") L.d("Delivering media button intent $intent")
intent.component = ComponentName(context, AuxioService::class.java) intent.component = ComponentName(context, AuxioService::class.java)
ContextCompat.startForegroundService(context, intent) ContextCompat.startForegroundService(context, intent)
} }

View file

@ -49,7 +49,7 @@ import org.oxycblt.auxio.playback.state.QueueChange
import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.util.newBroadcastPendingIntent import org.oxycblt.auxio.util.newBroadcastPendingIntent
import org.oxycblt.auxio.util.newMainPendingIntent import org.oxycblt.auxio.util.newMainPendingIntent
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A component that mirrors the current playback state into the [MediaSessionCompat] and * A component that mirrors the current playback state into the [MediaSessionCompat] and
@ -210,10 +210,10 @@ private constructor(
* playback is currently occuring from all songs. * playback is currently occuring from all songs.
*/ */
private fun updateMediaMetadata(song: Song?, parent: MusicParent?) { private fun updateMediaMetadata(song: Song?, parent: MusicParent?) {
T.d("Updating media metadata to $song with $parent") L.d("Updating media metadata to $song with $parent")
if (song == null) { if (song == null) {
// Nothing playing, reset the MediaSession and close the notification. // Nothing playing, reset the MediaSession and close the notification.
T.d("Nothing playing, resetting media session") L.d("Nothing playing, resetting media session")
mediaSession.setMetadata(emptyMetadata) mediaSession.setMetadata(emptyMetadata)
return return
} }
@ -252,15 +252,15 @@ private constructor(
MediaSessionUID.SingleItem(song.album.uid).toString()) MediaSessionUID.SingleItem(song.album.uid).toString())
// These fields are nullable and so we must check first before adding them to the fields. // These fields are nullable and so we must check first before adding them to the fields.
song.track?.let { song.track?.let {
T.d("Adding track information") L.d("Adding track information")
builder.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, it.toLong()) builder.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, it.toLong())
} }
song.disc?.let { song.disc?.let {
T.d("Adding disc information") L.d("Adding disc information")
builder.putLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, it.number.toLong()) builder.putLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, it.number.toLong())
} }
song.date?.let { song.date?.let {
T.d("Adding date information") L.d("Adding date information")
builder.putString(MediaMetadataCompat.METADATA_KEY_DATE, it.toString()) builder.putString(MediaMetadataCompat.METADATA_KEY_DATE, it.toString())
builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, it.year.toLong()) builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, it.year.toLong())
} }
@ -272,7 +272,7 @@ private constructor(
song, song,
object : BitmapProvider.Target { object : BitmapProvider.Target {
override fun onCompleted(bitmap: Bitmap?) { override fun onCompleted(bitmap: Bitmap?) {
T.d("Bitmap loaded, applying media session and posting notification") L.d("Bitmap loaded, applying media session and posting notification")
if (bitmap != null) { if (bitmap != null) {
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap) builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap)
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap) builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
@ -300,13 +300,13 @@ private constructor(
// playback state. // playback state.
MediaSessionCompat.QueueItem(description, i.toLong()) MediaSessionCompat.QueueItem(description, i.toLong())
} }
T.d("Uploading ${queueItems.size} songs to MediaSession queue") L.d("Uploading ${queueItems.size} songs to MediaSession queue")
mediaSession.setQueue(queueItems) mediaSession.setQueue(queueItems)
} }
/** Invalidate the current [MediaSessionCompat]'s [PlaybackStateCompat]. */ /** Invalidate the current [MediaSessionCompat]'s [PlaybackStateCompat]. */
private fun invalidateSessionState() { private fun invalidateSessionState() {
T.d("Updating media session playback state") L.d("Updating media session playback state")
val state = val state =
// InternalPlayer.State handles position/state information. // InternalPlayer.State handles position/state information.
@ -322,7 +322,7 @@ private constructor(
val secondaryAction = val secondaryAction =
when (playbackSettings.notificationAction) { when (playbackSettings.notificationAction) {
ActionMode.SHUFFLE -> { ActionMode.SHUFFLE -> {
T.d("Using shuffle MediaSession action") L.d("Using shuffle MediaSession action")
PlaybackStateCompat.CustomAction.Builder( PlaybackStateCompat.CustomAction.Builder(
PlaybackActions.ACTION_INVERT_SHUFFLE, PlaybackActions.ACTION_INVERT_SHUFFLE,
context.getString(R.string.desc_shuffle), context.getString(R.string.desc_shuffle),
@ -333,7 +333,7 @@ private constructor(
}) })
} }
else -> { else -> {
T.d("Using repeat mode MediaSession action") L.d("Using repeat mode MediaSession action")
PlaybackStateCompat.CustomAction.Builder( PlaybackStateCompat.CustomAction.Builder(
PlaybackActions.ACTION_INC_REPEAT_MODE, PlaybackActions.ACTION_INC_REPEAT_MODE,
context.getString(R.string.desc_change_repeat), context.getString(R.string.desc_change_repeat),
@ -356,22 +356,22 @@ private constructor(
/** Invalidate the "secondary" action (i.e shuffle/repeat mode). */ /** Invalidate the "secondary" action (i.e shuffle/repeat mode). */
private fun invalidateSecondaryAction() { private fun invalidateSecondaryAction() {
T.d("Invalidating secondary action") L.d("Invalidating secondary action")
invalidateSessionState() invalidateSessionState()
when (playbackSettings.notificationAction) { when (playbackSettings.notificationAction) {
ActionMode.SHUFFLE -> { ActionMode.SHUFFLE -> {
T.d("Using shuffle notification action") L.d("Using shuffle notification action")
_notification.updateShuffled(playbackManager.isShuffled) _notification.updateShuffled(playbackManager.isShuffled)
} }
else -> { else -> {
T.d("Using repeat mode notification action") L.d("Using repeat mode notification action")
_notification.updateRepeatMode(playbackManager.repeatMode) _notification.updateRepeatMode(playbackManager.repeatMode)
} }
} }
if (!bitmapProvider.isBusy) { if (!bitmapProvider.isBusy) {
T.d("Not loading a bitmap, post the notification") L.d("Not loading a bitmap, post the notification")
foregroundListener.updateForeground(ForegroundListener.Change.MEDIA_SESSION) foregroundListener.updateForeground(ForegroundListener.Change.MEDIA_SESSION)
} }
} }
@ -423,7 +423,7 @@ private class PlaybackNotification(
* @param metadata The [MediaMetadataCompat] to display in this notification. * @param metadata The [MediaMetadataCompat] to display in this notification.
*/ */
fun updateMetadata(metadata: MediaMetadataCompat) { fun updateMetadata(metadata: MediaMetadataCompat) {
T.d("Updating shown metadata") L.d("Updating shown metadata")
setLargeIcon(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) setLargeIcon(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART))
setContentTitle(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)) setContentTitle(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE))
setContentText(metadata.getText(MediaMetadataCompat.METADATA_KEY_ARTIST)) setContentText(metadata.getText(MediaMetadataCompat.METADATA_KEY_ARTIST))
@ -436,7 +436,7 @@ private class PlaybackNotification(
* @param isPlaying Whether playback should be indicated as ongoing or paused. * @param isPlaying Whether playback should be indicated as ongoing or paused.
*/ */
fun updatePlaying(isPlaying: Boolean) { fun updatePlaying(isPlaying: Boolean) {
T.d("Updating playing state: $isPlaying") L.d("Updating playing state: $isPlaying")
mActions[2] = buildPlayPauseAction(context, isPlaying) mActions[2] = buildPlayPauseAction(context, isPlaying)
} }
@ -446,7 +446,7 @@ private class PlaybackNotification(
* @param repeatMode The current [RepeatMode]. * @param repeatMode The current [RepeatMode].
*/ */
fun updateRepeatMode(repeatMode: RepeatMode) { fun updateRepeatMode(repeatMode: RepeatMode) {
T.d("Applying repeat mode action: $repeatMode") L.d("Applying repeat mode action: $repeatMode")
mActions[0] = buildRepeatAction(context, repeatMode) mActions[0] = buildRepeatAction(context, repeatMode)
} }
@ -456,7 +456,7 @@ private class PlaybackNotification(
* @param isShuffled Whether the queue is currently shuffled or not. * @param isShuffled Whether the queue is currently shuffled or not.
*/ */
fun updateShuffled(isShuffled: Boolean) { fun updateShuffled(isShuffled: Boolean) {
T.d("Applying shuffle action: $isShuffled") L.d("Applying shuffle action: $isShuffled")
mActions[0] = buildShuffleAction(context, isShuffled) mActions[0] = buildShuffleAction(context, isShuffled)
} }

View file

@ -20,7 +20,6 @@ package org.oxycblt.auxio.playback.service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.support.v4.media.MediaDescriptionCompat import android.support.v4.media.MediaDescriptionCompat
@ -47,7 +46,7 @@ import org.oxycblt.auxio.playback.state.PlaybackCommand
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.state.ShuffleMode import org.oxycblt.auxio.playback.state.ShuffleMode
import timber.log.Timber as T import timber.log.Timber as L
class MediaSessionInterface class MediaSessionInterface
@Inject @Inject
@ -82,7 +81,7 @@ constructor(
val parentUid = val parentUid =
extras?.getString(MusicBrowser.KEY_CHILD_OF)?.let { MediaSessionUID.fromString(it) } extras?.getString(MusicBrowser.KEY_CHILD_OF)?.let { MediaSessionUID.fromString(it) }
val command = expandUidIntoCommand(uid, parentUid) val command = expandUidIntoCommand(uid, parentUid)
T.d(extras?.getString(MusicBrowser.KEY_CHILD_OF)) L.d(extras?.getString(MusicBrowser.KEY_CHILD_OF))
playbackManager.play(requireNotNull(command) { "Invalid playback configuration" }) playbackManager.play(requireNotNull(command) { "Invalid playback configuration" })
} }
@ -295,9 +294,11 @@ constructor(
private fun expandSongIntoCommand(music: Song, parent: MusicParent?) = private fun expandSongIntoCommand(music: Song, parent: MusicParent?) =
when (parent) { when (parent) {
is Album -> commandFactory.songFromAlbum(music, ShuffleMode.IMPLICIT) is Album -> commandFactory.songFromAlbum(music, ShuffleMode.IMPLICIT)
is Artist -> commandFactory.songFromArtist(music, parent, ShuffleMode.IMPLICIT) is Artist ->
commandFactory.songFromArtist(music, parent, ShuffleMode.IMPLICIT)
?: commandFactory.songFromArtist(music, music.artists[0], ShuffleMode.IMPLICIT) ?: commandFactory.songFromArtist(music, music.artists[0], ShuffleMode.IMPLICIT)
is Genre -> commandFactory.songFromGenre(music, parent, ShuffleMode.IMPLICIT) is Genre ->
commandFactory.songFromGenre(music, parent, ShuffleMode.IMPLICIT)
?: commandFactory.songFromGenre(music, music.genres[0], ShuffleMode.IMPLICIT) ?: commandFactory.songFromGenre(music, music.genres[0], ShuffleMode.IMPLICIT)
is Playlist -> commandFactory.songFromPlaylist(music, parent, ShuffleMode.IMPLICIT) is Playlist -> commandFactory.songFromPlaylist(music, parent, ShuffleMode.IMPLICIT)
null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT) null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT)

View file

@ -28,7 +28,7 @@ import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.playback.state.DeferredPlayback import org.oxycblt.auxio.playback.state.DeferredPlayback
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.widgets.WidgetComponent import org.oxycblt.auxio.widgets.WidgetComponent
import timber.log.Timber as T import timber.log.Timber as L
class PlaybackServiceFragment class PlaybackServiceFragment
private constructor( private constructor(
@ -86,7 +86,7 @@ private constructor(
fun start(startedBy: Int) { fun start(startedBy: Int) {
// At minimum we want to ensure an active playback state. // At minimum we want to ensure an active playback state.
// TODO: Possibly also force to go foreground? // TODO: Possibly also force to go foreground?
T.d("Handling non-native start.") L.d("Handling non-native start.")
val action = val action =
when (startedBy) { when (startedBy) {
IntegerTable.START_ID_ACTIVITY -> null IntegerTable.START_ID_ACTIVITY -> null
@ -97,7 +97,7 @@ private constructor(
else -> DeferredPlayback.RestoreState(play = false) else -> DeferredPlayback.RestoreState(play = false)
} }
if (action != null) { if (action != null) {
T.d("Initing service fragment using action $action") L.d("Initing service fragment using action $action")
playbackManager.playDeferred(action) playbackManager.playDeferred(action)
} }
} }

View file

@ -29,7 +29,7 @@ import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.widgets.WidgetComponent import org.oxycblt.auxio.widgets.WidgetComponent
import org.oxycblt.auxio.widgets.WidgetProvider import org.oxycblt.auxio.widgets.WidgetProvider
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A [BroadcastReceiver] for receiving playback-specific [Intent]s from the system that require an * A [BroadcastReceiver] for receiving playback-specific [Intent]s from the system that require an
@ -76,7 +76,7 @@ private constructor(
// 3. Some internal framework thing that also handles bluetooth headsets // 3. Some internal framework thing that also handles bluetooth headsets
// Just use ACTION_HEADSET_PLUG. // Just use ACTION_HEADSET_PLUG.
AudioManager.ACTION_HEADSET_PLUG -> { AudioManager.ACTION_HEADSET_PLUG -> {
T.d("Received headset plug event") L.d("Received headset plug event")
when (intent.getIntExtra("state", -1)) { when (intent.getIntExtra("state", -1)) {
0 -> pauseFromHeadsetPlug() 0 -> pauseFromHeadsetPlug()
1 -> playFromHeadsetPlug() 1 -> playFromHeadsetPlug()
@ -85,37 +85,37 @@ private constructor(
initialHeadsetPlugEventHandled = true initialHeadsetPlugEventHandled = true
} }
AudioManager.ACTION_AUDIO_BECOMING_NOISY -> { AudioManager.ACTION_AUDIO_BECOMING_NOISY -> {
T.d("Received Headset noise event") L.d("Received Headset noise event")
pauseFromHeadsetPlug() pauseFromHeadsetPlug()
} }
// --- AUXIO EVENTS --- // --- AUXIO EVENTS ---
PlaybackActions.ACTION_PLAY_PAUSE -> { PlaybackActions.ACTION_PLAY_PAUSE -> {
T.d("Received play event") L.d("Received play event")
playbackManager.playing(!playbackManager.progression.isPlaying) playbackManager.playing(!playbackManager.progression.isPlaying)
} }
PlaybackActions.ACTION_INC_REPEAT_MODE -> { PlaybackActions.ACTION_INC_REPEAT_MODE -> {
T.d("Received repeat mode event") L.d("Received repeat mode event")
playbackManager.repeatMode(playbackManager.repeatMode.increment()) playbackManager.repeatMode(playbackManager.repeatMode.increment())
} }
PlaybackActions.ACTION_INVERT_SHUFFLE -> { PlaybackActions.ACTION_INVERT_SHUFFLE -> {
T.d("Received shuffle event") L.d("Received shuffle event")
playbackManager.shuffled(!playbackManager.isShuffled) playbackManager.shuffled(!playbackManager.isShuffled)
} }
PlaybackActions.ACTION_SKIP_PREV -> { PlaybackActions.ACTION_SKIP_PREV -> {
T.d("Received skip previous event") L.d("Received skip previous event")
playbackManager.prev() playbackManager.prev()
} }
PlaybackActions.ACTION_SKIP_NEXT -> { PlaybackActions.ACTION_SKIP_NEXT -> {
T.d("Received skip next event") L.d("Received skip next event")
playbackManager.next() playbackManager.next()
} }
PlaybackActions.ACTION_EXIT -> { PlaybackActions.ACTION_EXIT -> {
T.d("Received exit event") L.d("Received exit event")
playbackManager.endSession() playbackManager.endSession()
} }
WidgetProvider.ACTION_WIDGET_UPDATE -> { WidgetProvider.ACTION_WIDGET_UPDATE -> {
T.d("Received widget update event") L.d("Received widget update event")
widgetComponent.update() widgetComponent.update()
} }
} }
@ -128,14 +128,14 @@ private constructor(
if (playbackSettings.headsetAutoplay && if (playbackSettings.headsetAutoplay &&
playbackManager.currentSong != null && playbackManager.currentSong != null &&
initialHeadsetPlugEventHandled) { initialHeadsetPlugEventHandled) {
T.d("Device connected, resuming") L.d("Device connected, resuming")
playbackManager.playing(true) playbackManager.playing(true)
} }
} }
private fun pauseFromHeadsetPlug() { private fun pauseFromHeadsetPlug() {
if (playbackManager.currentSong != null) { if (playbackManager.currentSong != null) {
T.d("Device disconnected, pausing") L.d("Device disconnected, pausing")
playbackManager.playing(false) playbackManager.playing(false)
} }
} }

View file

@ -18,6 +18,7 @@
package org.oxycblt.auxio.playback.state package org.oxycblt.auxio.playback.state
import javax.inject.Inject
import org.oxycblt.auxio.list.ListSettings import org.oxycblt.auxio.list.ListSettings
import org.oxycblt.auxio.list.sort.Sort import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
@ -28,7 +29,6 @@ import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import javax.inject.Inject
/** /**
* A playback command that can be passed to [PlaybackStateManager] to start new playback. * A playback command that can be passed to [PlaybackStateManager] to start new playback.

View file

@ -25,7 +25,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.state.PlaybackStateManager.Listener import org.oxycblt.auxio.playback.state.PlaybackStateManager.Listener
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Core playback state controller class. * Core playback state controller class.
@ -387,11 +387,11 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun addListener(listener: Listener) { override fun addListener(listener: Listener) {
T.d("Adding $listener to listeners") L.d("Adding $listener to listeners")
listeners.add(listener) listeners.add(listener)
if (isInitialized) { if (isInitialized) {
T.d("Sending initial state to $listener") L.d("Sending initial state to $listener")
listener.onNewPlayback( listener.onNewPlayback(
stateMirror.parent, stateMirror.queue, stateMirror.index, stateMirror.isShuffled) stateMirror.parent, stateMirror.queue, stateMirror.index, stateMirror.isShuffled)
listener.onProgressionChanged(stateMirror.progression) listener.onProgressionChanged(stateMirror.progression)
@ -401,16 +401,16 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun removeListener(listener: Listener) { override fun removeListener(listener: Listener) {
T.d("Removing $listener from listeners") L.d("Removing $listener from listeners")
if (!listeners.remove(listener)) { if (!listeners.remove(listener)) {
T.w("Listener $listener was not added prior, cannot remove") L.w("Listener $listener was not added prior, cannot remove")
} }
} }
@Synchronized @Synchronized
override fun registerStateHolder(stateHolder: PlaybackStateHolder) { override fun registerStateHolder(stateHolder: PlaybackStateHolder) {
if (this.stateHolder != null) { if (this.stateHolder != null) {
T.w("Internal player is already registered") L.w("Internal player is already registered")
return return
} }
@ -429,11 +429,11 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun unregisterStateHolder(stateHolder: PlaybackStateHolder) { override fun unregisterStateHolder(stateHolder: PlaybackStateHolder) {
if (this.stateHolder !== stateHolder) { if (this.stateHolder !== stateHolder) {
T.w("Given internal player did not match current internal player") L.w("Given internal player did not match current internal player")
return return
} }
T.d("Unregistering internal player $stateHolder") L.d("Unregistering internal player $stateHolder")
this.stateHolder = null this.stateHolder = null
} }
@ -443,7 +443,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun play(command: PlaybackCommand) { override fun play(command: PlaybackCommand) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Playing $command") L.d("Playing $command")
// Played something, so we are initialized now // Played something, so we are initialized now
isInitialized = true isInitialized = true
stateHolder.newPlayback(command) stateHolder.newPlayback(command)
@ -454,32 +454,32 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun next() { override fun next() {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Going to next song") L.d("Going to next song")
stateHolder.next() stateHolder.next()
} }
@Synchronized @Synchronized
override fun prev() { override fun prev() {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Going to previous song") L.d("Going to previous song")
stateHolder.prev() stateHolder.prev()
} }
@Synchronized @Synchronized
override fun goto(index: Int) { override fun goto(index: Int) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Going to index $index") L.d("Going to index $index")
stateHolder.goto(index) stateHolder.goto(index)
} }
@Synchronized @Synchronized
override fun playNext(songs: List<Song>) { override fun playNext(songs: List<Song>) {
if (currentSong == null) { if (currentSong == null) {
T.d("Nothing playing, short-circuiting to new playback") L.d("Nothing playing, short-circuiting to new playback")
play(QueueCommand(songs)) play(QueueCommand(songs))
} else { } else {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Adding ${songs.size} songs to start of queue") L.d("Adding ${songs.size} songs to start of queue")
stateHolder.playNext(songs, StateAck.PlayNext(stateMirror.index + 1, songs.size)) stateHolder.playNext(songs, StateAck.PlayNext(stateMirror.index + 1, songs.size))
} }
} }
@ -487,11 +487,11 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun addToQueue(songs: List<Song>) { override fun addToQueue(songs: List<Song>) {
if (currentSong == null) { if (currentSong == null) {
T.d("Nothing playing, short-circuiting to new playback") L.d("Nothing playing, short-circuiting to new playback")
play(QueueCommand(songs)) play(QueueCommand(songs))
} else { } else {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Adding ${songs.size} songs to end of queue") L.d("Adding ${songs.size} songs to end of queue")
stateHolder.addToQueue(songs, StateAck.AddToQueue(queue.size, songs.size)) stateHolder.addToQueue(songs, StateAck.AddToQueue(queue.size, songs.size))
} }
} }
@ -505,21 +505,21 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun moveQueueItem(src: Int, dst: Int) { override fun moveQueueItem(src: Int, dst: Int) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Moving item $src to position $dst") L.d("Moving item $src to position $dst")
stateHolder.move(src, dst, StateAck.Move(src, dst)) stateHolder.move(src, dst, StateAck.Move(src, dst))
} }
@Synchronized @Synchronized
override fun removeQueueItem(at: Int) { override fun removeQueueItem(at: Int) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Removing item at $at") L.d("Removing item at $at")
stateHolder.remove(at, StateAck.Remove(at)) stateHolder.remove(at, StateAck.Remove(at))
} }
@Synchronized @Synchronized
override fun shuffled(shuffled: Boolean) { override fun shuffled(shuffled: Boolean) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Reordering queue [shuffled=$shuffled]") L.d("Reordering queue [shuffled=$shuffled]")
stateHolder.shuffled(shuffled) stateHolder.shuffled(shuffled)
} }
@ -529,7 +529,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
override fun playDeferred(action: DeferredPlayback) { override fun playDeferred(action: DeferredPlayback) {
val stateHolder = stateHolder val stateHolder = stateHolder
if (stateHolder == null || !stateHolder.handleDeferred(action)) { if (stateHolder == null || !stateHolder.handleDeferred(action)) {
T.d("Internal player not present or did not consume action, waiting") L.d("Internal player not present or did not consume action, waiting")
pendingDeferredPlayback = action pendingDeferredPlayback = action
} }
} }
@ -537,12 +537,12 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun requestAction(stateHolder: PlaybackStateHolder) { override fun requestAction(stateHolder: PlaybackStateHolder) {
if (BuildConfig.DEBUG && this.stateHolder !== stateHolder) { if (BuildConfig.DEBUG && this.stateHolder !== stateHolder) {
T.w("Given internal player did not match current internal player") L.w("Given internal player did not match current internal player")
return return
} }
if (pendingDeferredPlayback?.let(stateHolder::handleDeferred) == true) { if (pendingDeferredPlayback?.let(stateHolder::handleDeferred) == true) {
T.d("Pending action consumed") L.d("Pending action consumed")
pendingDeferredPlayback = null pendingDeferredPlayback = null
} }
} }
@ -550,35 +550,35 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
@Synchronized @Synchronized
override fun playing(isPlaying: Boolean) { override fun playing(isPlaying: Boolean) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Updating playing state to $isPlaying") L.d("Updating playing state to $isPlaying")
stateHolder.playing(isPlaying) stateHolder.playing(isPlaying)
} }
@Synchronized @Synchronized
override fun repeatMode(repeatMode: RepeatMode) { override fun repeatMode(repeatMode: RepeatMode) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Updating repeat mode to $repeatMode") L.d("Updating repeat mode to $repeatMode")
stateHolder.repeatMode(repeatMode) stateHolder.repeatMode(repeatMode)
} }
@Synchronized @Synchronized
override fun seekTo(positionMs: Long) { override fun seekTo(positionMs: Long) {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Seeking to ${positionMs}ms") L.d("Seeking to ${positionMs}ms")
stateHolder.seekTo(positionMs) stateHolder.seekTo(positionMs)
} }
@Synchronized @Synchronized
override fun endSession() { override fun endSession() {
val stateHolder = stateHolder ?: return val stateHolder = stateHolder ?: return
T.d("Ending session") L.d("Ending session")
stateHolder.endSession() stateHolder.endSession()
} }
@Synchronized @Synchronized
override fun ack(stateHolder: PlaybackStateHolder, ack: StateAck) { override fun ack(stateHolder: PlaybackStateHolder, ack: StateAck) {
if (BuildConfig.DEBUG && this.stateHolder !== stateHolder) { if (BuildConfig.DEBUG && this.stateHolder !== stateHolder) {
T.w("Given internal player did not match current internal player") L.w("Given internal player did not match current internal player")
return return
} }
@ -729,7 +729,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
destructive: Boolean destructive: Boolean
) { ) {
if (isInitialized && !destructive) { if (isInitialized && !destructive) {
T.w("Already initialized, cannot apply saved state") L.w("Already initialized, cannot apply saved state")
return return
} }
@ -751,7 +751,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
} }
} }
T.d("Created adjustment mapping [max shift=$currentShift]") L.d("Created adjustment mapping [max shift=$currentShift]")
val shuffledMapping = val shuffledMapping =
savedState.shuffledMapping.mapNotNullTo(mutableListOf()) { index -> savedState.shuffledMapping.mapNotNullTo(mutableListOf()) { index ->
@ -775,7 +775,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
index-- index--
} }
T.d("Corrected index: ${savedState.index} -> $index") L.d("Corrected index: ${savedState.index} -> $index")
check(shuffledMapping.all { it in heap.indices }) { check(shuffledMapping.all { it in heap.indices }) {
"Queue inconsistency detected: Shuffled mapping indices out of heap bounds" "Queue inconsistency detected: Shuffled mapping indices out of heap bounds"

View file

@ -21,12 +21,10 @@ package org.oxycblt.auxio.playback.ui
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.material.R as MR
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.motion.MotionUtils
import org.oxycblt.auxio.ui.RippleFixMaterialButton import org.oxycblt.auxio.ui.RippleFixMaterialButton
import timber.log.Timber as T import org.oxycblt.auxio.ui.StationaryAnim
import timber.log.Timber as L
/** /**
* A [MaterialButton] that automatically morphs from a circle to a squircle shape appearance when * A [MaterialButton] that automatically morphs from a circle to a squircle shape appearance when
@ -47,13 +45,7 @@ class AnimatedMaterialButton : RippleFixMaterialButton {
private var currentCornerRadiusRatio = 0f private var currentCornerRadiusRatio = 0f
private var animator: ValueAnimator? = null private var animator: ValueAnimator? = null
private val anim = StationaryAnim.forMediumComponent(context)
private val matInterpolator =
MotionUtils.resolveThemeInterpolator(
context, MR.attr.motionEasingStandardInterpolator, FastOutSlowInInterpolator())
private val matDuration =
MotionUtils.resolveThemeDuration(context, MR.attr.motionDurationMedium2, 300)
override fun setActivated(activated: Boolean) { override fun setActivated(activated: Boolean) {
super.setActivated(activated) super.setActivated(activated)
@ -62,20 +54,17 @@ class AnimatedMaterialButton : RippleFixMaterialButton {
val targetRadius = if (activated) 0.3f else 0.5f val targetRadius = if (activated) 0.3f else 0.5f
if (!isLaidOut) { if (!isLaidOut) {
// Not laid out, initialize it without animation before drawing. // Not laid out, initialize it without animation before drawing.
T.d("Not laid out, immediately updating corner radius") L.d("Not laid out, immediately updating corner radius")
updateCornerRadiusRatio(targetRadius) updateCornerRadiusRatio(targetRadius)
return return
} }
T.d("Starting corner radius animation") L.d("Starting corner radius animation")
animator?.cancel() animator?.cancel()
animator = animator =
ValueAnimator.ofFloat(currentCornerRadiusRatio, targetRadius).apply { anim
duration = matDuration.toLong() .genericFloat(currentCornerRadiusRatio, targetRadius, ::updateCornerRadiusRatio)
interpolator = matInterpolator .also { it.start() }
addUpdateListener { updateCornerRadiusRatio(animatedValue as Float) }
start()
}
} }
private fun updateCornerRadiusRatio(ratio: Float) { private fun updateCornerRadiusRatio(ratio: Float) {

View file

@ -25,7 +25,7 @@ import kotlin.math.max
import org.oxycblt.auxio.databinding.ViewSeekBarBinding import org.oxycblt.auxio.databinding.ViewSeekBarBinding
import org.oxycblt.auxio.playback.formatDurationDs import org.oxycblt.auxio.playback.formatDurationDs
import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.inflater
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* A wrapper around [Slider] that shows position and duration values and sanitizes input to reduce * A wrapper around [Slider] that shows position and duration values and sanitizes input to reduce
@ -81,11 +81,11 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
// zero, use 1 instead and disable the SeekBar. // zero, use 1 instead and disable the SeekBar.
val to = max(value, 1) val to = max(value, 1)
isEnabled = value > 0 isEnabled = value > 0
T.d("Value sanitization finished [to=$to, enabled=$isEnabled]") L.d("Value sanitization finished [to=$to, enabled=$isEnabled]")
// Sanity check 2: If the current value exceeds the new duration value, clamp it // Sanity check 2: If the current value exceeds the new duration value, clamp it
// down so that we don't crash and instead have an annoying visual flicker. // down so that we don't crash and instead have an annoying visual flicker.
if (positionDs > to) { if (positionDs > to) {
T.d("Clamping invalid position [current: $positionDs new max: $to]") L.d("Clamping invalid position [current: $positionDs new max: $to]")
binding.seekBarSlider.value = to.toFloat() binding.seekBarSlider.value = to.toFloat()
} }
binding.seekBarSlider.valueTo = to.toFloat() binding.seekBarSlider.valueTo = to.toFloat()
@ -93,14 +93,14 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
} }
override fun onStartTrackingTouch(slider: Slider) { override fun onStartTrackingTouch(slider: Slider) {
T.d("Starting seek mode") L.d("Starting seek mode")
// User has begun seeking, place the SeekBar into a "Suspended" mode in which no // User has begun seeking, place the SeekBar into a "Suspended" mode in which no
// position updates are sent and is indicated by the position value turning accented. // position updates are sent and is indicated by the position value turning accented.
isActivated = true isActivated = true
} }
override fun onStopTrackingTouch(slider: Slider) { override fun onStopTrackingTouch(slider: Slider) {
T.d("Confirming seek") L.d("Confirming seek")
// End of seek event, send off new value to listener. // End of seek event, send off new value to listener.
isActivated = false isActivated = false
listener?.onSeekConfirmed(slider.value.toLong()) listener?.onSeekConfirmed(slider.value.toLong())

View file

@ -29,7 +29,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.info.Name import org.oxycblt.auxio.music.info.Name
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Implements the fuzzy-ish searching algorithm used in the search view. * Implements the fuzzy-ish searching algorithm used in the search view.
@ -67,7 +67,7 @@ interface SearchEngine {
class SearchEngineImpl @Inject constructor(@ApplicationContext private val context: Context) : class SearchEngineImpl @Inject constructor(@ApplicationContext private val context: Context) :
SearchEngine { SearchEngine {
override suspend fun search(items: SearchEngine.Items, query: String): SearchEngine.Items { override suspend fun search(items: SearchEngine.Items, query: String): SearchEngine.Items {
T.d("Launching search for $query") L.d("Launching search for $query")
return SearchEngine.Items( return SearchEngine.Items(
songs = songs =
items.songs?.searchListImpl(query) { q, song -> items.songs?.searchListImpl(query) { q, song ->

View file

@ -64,7 +64,7 @@ import org.oxycblt.auxio.util.getSystemServiceCompat
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.setFullWidthLookup import org.oxycblt.auxio.util.setFullWidthLookup
import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.showToast
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* The [ListFragment] providing search functionality for the music library. * The [ListFragment] providing search functionality for the music library.
@ -108,11 +108,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
getContentLauncher = getContentLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri == null) { if (uri == null) {
T.w("No URI returned from file picker") L.w("No URI returned from file picker")
return@registerForActivityResult return@registerForActivityResult
} }
T.d("Received playlist URI $uri") L.d("Received playlist URI $uri")
musicModel.importPlaylist(uri, pendingImportTarget) musicModel.importPlaylist(uri, pendingImportTarget)
} }
@ -139,7 +139,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
if (!launchedKeyboard) { if (!launchedKeyboard) {
// Auto-open the keyboard when this view is shown // Auto-open the keyboard when this view is shown
T.d("Keyboard is not shown yet") L.d("Keyboard is not shown yet")
showKeyboard(this) showKeyboard(this)
launchedKeyboard = true launchedKeyboard = true
} }
@ -184,7 +184,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
if (item.itemId != R.id.submenu_filtering) { if (item.itemId != R.id.submenu_filtering) {
// Is a change in filter mode and not just a junk submenu click, update // Is a change in filter mode and not just a junk submenu click, update
// the filtering within SearchViewModel. // the filtering within SearchViewModel.
T.d("Filter mode selected") L.d("Filter mode selected")
item.isChecked = true item.isChecked = true
searchModel.setFilterOptionId(item.itemId) searchModel.setFilterOptionId(item.itemId)
return true return true
@ -222,7 +222,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
// I would make it so that the position is only scrolled back to the top when // I would make it so that the position is only scrolled back to the top when
// the query actually changes instead of once every re-creation event, but sadly // the query actually changes instead of once every re-creation event, but sadly
// that doesn't seem possible. // that doesn't seem possible.
T.d("Update finished, scrolling to top") L.d("Update finished, scrolling to top")
binding.searchRecycler.scrollToPosition(0) binding.searchRecycler.scrollToPosition(0)
} }
} }
@ -230,39 +230,39 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
private fun handleShow(show: Show?) { private fun handleShow(show: Show?) {
when (show) { when (show) {
is Show.SongDetails -> { is Show.SongDetails -> {
T.d("Navigating to ${show.song}") L.d("Navigating to ${show.song}")
findNavController().navigateSafe(SearchFragmentDirections.showSong(show.song.uid)) findNavController().navigateSafe(SearchFragmentDirections.showSong(show.song.uid))
} }
is Show.SongAlbumDetails -> { is Show.SongAlbumDetails -> {
T.d("Navigating to the album of ${show.song}") L.d("Navigating to the album of ${show.song}")
findNavController() findNavController()
.navigateSafe(SearchFragmentDirections.showAlbum(show.song.album.uid)) .navigateSafe(SearchFragmentDirections.showAlbum(show.song.album.uid))
} }
is Show.AlbumDetails -> { is Show.AlbumDetails -> {
T.d("Navigating to ${show.album}") L.d("Navigating to ${show.album}")
findNavController().navigateSafe(SearchFragmentDirections.showAlbum(show.album.uid)) findNavController().navigateSafe(SearchFragmentDirections.showAlbum(show.album.uid))
} }
is Show.ArtistDetails -> { is Show.ArtistDetails -> {
T.d("Navigating to ${show.artist}") L.d("Navigating to ${show.artist}")
findNavController() findNavController()
.navigateSafe(SearchFragmentDirections.showArtist(show.artist.uid)) .navigateSafe(SearchFragmentDirections.showArtist(show.artist.uid))
} }
is Show.SongArtistDecision -> { is Show.SongArtistDecision -> {
T.d("Navigating to artist choices for ${show.song}") L.d("Navigating to artist choices for ${show.song}")
findNavController() findNavController()
.navigateSafe(SearchFragmentDirections.showArtistChoices(show.song.uid)) .navigateSafe(SearchFragmentDirections.showArtistChoices(show.song.uid))
} }
is Show.AlbumArtistDecision -> { is Show.AlbumArtistDecision -> {
T.d("Navigating to artist choices for ${show.album}") L.d("Navigating to artist choices for ${show.album}")
findNavController() findNavController()
.navigateSafe(SearchFragmentDirections.showArtistChoices(show.album.uid)) .navigateSafe(SearchFragmentDirections.showArtistChoices(show.album.uid))
} }
is Show.GenreDetails -> { is Show.GenreDetails -> {
T.d("Navigating to ${show.genre}") L.d("Navigating to ${show.genre}")
findNavController().navigateSafe(SearchFragmentDirections.showGenre(show.genre.uid)) findNavController().navigateSafe(SearchFragmentDirections.showGenre(show.genre.uid))
} }
is Show.PlaylistDetails -> { is Show.PlaylistDetails -> {
T.d("Navigating to ${show.playlist}") L.d("Navigating to ${show.playlist}")
findNavController() findNavController()
.navigateSafe(SearchFragmentDirections.showPlaylist(show.playlist.uid)) .navigateSafe(SearchFragmentDirections.showPlaylist(show.playlist.uid))
} }
@ -296,7 +296,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
binding.searchSelectionToolbar.title = getString(R.string.fmt_selected, selected.size) binding.searchSelectionToolbar.title = getString(R.string.fmt_selected, selected.size)
if (binding.searchToolbar.setVisible(R.id.search_selection_toolbar)) { if (binding.searchToolbar.setVisible(R.id.search_selection_toolbar)) {
// New selection started, show the keyboard to make selection easier. // New selection started, show the keyboard to make selection easier.
T.d("Significant selection occurred, hiding keyboard") L.d("Significant selection occurred, hiding keyboard")
hideKeyboard() hideKeyboard()
} }
} else { } else {
@ -309,7 +309,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaylistDecision.Import -> { is PlaylistDecision.Import -> {
T.d("Importing playlist") L.d("Importing playlist")
pendingImportTarget = decision.target pendingImportTarget = decision.target
requireNotNull(getContentLauncher) { requireNotNull(getContentLauncher) {
"Content picker launcher was not available" "Content picker launcher was not available"
@ -319,7 +319,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
return return
} }
is PlaylistDecision.Rename -> { is PlaylistDecision.Rename -> {
T.d("Renaming ${decision.playlist}") L.d("Renaming ${decision.playlist}")
SearchFragmentDirections.renamePlaylist( SearchFragmentDirections.renamePlaylist(
decision.playlist.uid, decision.playlist.uid,
decision.template, decision.template,
@ -327,15 +327,15 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
decision.reason) decision.reason)
} }
is PlaylistDecision.Delete -> { is PlaylistDecision.Delete -> {
T.d("Deleting ${decision.playlist}") L.d("Deleting ${decision.playlist}")
SearchFragmentDirections.deletePlaylist(decision.playlist.uid) SearchFragmentDirections.deletePlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Export -> { is PlaylistDecision.Export -> {
T.d("Exporting ${decision.playlist}") L.d("Exporting ${decision.playlist}")
SearchFragmentDirections.exportPlaylist(decision.playlist.uid) SearchFragmentDirections.exportPlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
T.d("Adding ${decision.songs.size} to a playlist") L.d("Adding ${decision.songs.size} to a playlist")
SearchFragmentDirections.addToPlaylist( SearchFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
@ -361,11 +361,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
val directions = val directions =
when (decision) { when (decision) {
is PlaybackDecision.PlayFromArtist -> { is PlaybackDecision.PlayFromArtist -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
SearchFragmentDirections.playFromArtist(decision.song.uid) SearchFragmentDirections.playFromArtist(decision.song.uid)
} }
is PlaybackDecision.PlayFromGenre -> { is PlaybackDecision.PlayFromGenre -> {
T.d("Launching play from artist dialog for $decision") L.d("Launching play from artist dialog for $decision")
SearchFragmentDirections.playFromGenre(decision.song.uid) SearchFragmentDirections.playFromGenre(decision.song.uid)
} }
} }
@ -378,7 +378,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
* @param view The [View] to focus the keyboard on. * @param view The [View] to focus the keyboard on.
*/ */
private fun showKeyboard(view: View) { private fun showKeyboard(view: View) {
T.d("Launching keyboard") L.d("Launching keyboard")
view.apply { view.apply {
requestFocus() requestFocus()
postDelayed(200) { postDelayed(200) {
@ -390,7 +390,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
/** Safely hide the keyboard from this view. */ /** Safely hide the keyboard from this view. */
private fun hideKeyboard() { private fun hideKeyboard() {
T.d("Hiding keyboard") L.d("Hiding keyboard")
requireNotNull(imm) { "InputMethodManager was not available" } requireNotNull(imm) { "InputMethodManager was not available" }
.hideSoftInputFromWindow(requireView().windowToken, InputMethodManager.HIDE_NOT_ALWAYS) .hideSoftInputFromWindow(requireView().windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
} }

View file

@ -40,7 +40,7 @@ import org.oxycblt.auxio.music.device.DeviceLibrary
import org.oxycblt.auxio.music.user.UserLibrary import org.oxycblt.auxio.music.user.UserLibrary
import org.oxycblt.auxio.playback.PlaySong import org.oxycblt.auxio.playback.PlaySong
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* An [ViewModel] that keeps performs search operations and tracks their results. * An [ViewModel] that keeps performs search operations and tracks their results.
@ -79,7 +79,7 @@ constructor(
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
if (changes.deviceLibrary || changes.userLibrary) { if (changes.deviceLibrary || changes.userLibrary) {
T.d("Music changed, re-searching library") L.d("Music changed, re-searching library")
search(lastQuery) search(lastQuery)
} }
} }
@ -98,13 +98,13 @@ constructor(
val deviceLibrary = musicRepository.deviceLibrary val deviceLibrary = musicRepository.deviceLibrary
val userLibrary = musicRepository.userLibrary val userLibrary = musicRepository.userLibrary
if (query.isNullOrEmpty() || deviceLibrary == null || userLibrary == null) { if (query.isNullOrEmpty() || deviceLibrary == null || userLibrary == null) {
T.d("Cannot search for the current query, aborting") L.d("Cannot search for the current query, aborting")
_searchResults.value = listOf() _searchResults.value = listOf()
return return
} }
// Searching is time-consuming, so do it in the background. // Searching is time-consuming, so do it in the background.
T.d("Searching music library for $query") L.d("Searching music library for $query")
currentSearchJob = currentSearchJob =
viewModelScope.launch { viewModelScope.launch {
_searchResults.value = _searchResults.value =
@ -122,7 +122,7 @@ constructor(
val items = val items =
if (filter == null) { if (filter == null) {
// A nulled filter type means to not filter anything. // A nulled filter type means to not filter anything.
T.d("No filter specified, using entire library") L.d("No filter specified, using entire library")
SearchEngine.Items( SearchEngine.Items(
deviceLibrary.songs, deviceLibrary.songs,
deviceLibrary.albums, deviceLibrary.albums,
@ -130,7 +130,7 @@ constructor(
deviceLibrary.genres, deviceLibrary.genres,
userLibrary.playlists) userLibrary.playlists)
} else { } else {
T.d("Filter specified, reducing library") L.d("Filter specified, reducing library")
SearchEngine.Items( SearchEngine.Items(
songs = if (filter == MusicType.SONGS) deviceLibrary.songs else null, songs = if (filter == MusicType.SONGS) deviceLibrary.songs else null,
albums = if (filter == MusicType.ALBUMS) deviceLibrary.albums else null, albums = if (filter == MusicType.ALBUMS) deviceLibrary.albums else null,
@ -143,13 +143,13 @@ constructor(
return buildList { return buildList {
results.artists?.let { results.artists?.let {
T.d("Adding ${it.size} artists to search results") L.d("Adding ${it.size} artists to search results")
val header = BasicHeader(R.string.lbl_artists) val header = BasicHeader(R.string.lbl_artists)
add(header) add(header)
addAll(SORT.artists(it)) addAll(SORT.artists(it))
} }
results.albums?.let { results.albums?.let {
T.d("Adding ${it.size} albums to search results") L.d("Adding ${it.size} albums to search results")
val header = BasicHeader(R.string.lbl_albums) val header = BasicHeader(R.string.lbl_albums)
if (isNotEmpty()) { if (isNotEmpty()) {
add(PlainDivider(header)) add(PlainDivider(header))
@ -159,7 +159,7 @@ constructor(
addAll(SORT.albums(it)) addAll(SORT.albums(it))
} }
results.playlists?.let { results.playlists?.let {
T.d("Adding ${it.size} playlists to search results") L.d("Adding ${it.size} playlists to search results")
val header = BasicHeader(R.string.lbl_playlists) val header = BasicHeader(R.string.lbl_playlists)
if (isNotEmpty()) { if (isNotEmpty()) {
add(PlainDivider(header)) add(PlainDivider(header))
@ -169,7 +169,7 @@ constructor(
addAll(SORT.playlists(it)) addAll(SORT.playlists(it))
} }
results.genres?.let { results.genres?.let {
T.d("Adding ${it.size} genres to search results") L.d("Adding ${it.size} genres to search results")
val header = BasicHeader(R.string.lbl_genres) val header = BasicHeader(R.string.lbl_genres)
if (isNotEmpty()) { if (isNotEmpty()) {
add(PlainDivider(header)) add(PlainDivider(header))
@ -179,7 +179,7 @@ constructor(
addAll(SORT.genres(it)) addAll(SORT.genres(it))
} }
results.songs?.let { results.songs?.let {
T.d("Adding ${it.size} songs to search results") L.d("Adding ${it.size} songs to search results")
val header = BasicHeader(R.string.lbl_songs) val header = BasicHeader(R.string.lbl_songs)
if (isNotEmpty()) { if (isNotEmpty()) {
add(PlainDivider(header)) add(PlainDivider(header))
@ -225,7 +225,7 @@ constructor(
R.id.option_filter_all -> null R.id.option_filter_all -> null
else -> error("Invalid option ID provided") else -> error("Invalid option ID provided")
} }
T.d("Updating filter type to $newFilter") L.d("Updating filter type to $newFilter")
searchSettings.filterTo = newFilter searchSettings.filterTo = newFilter
search(lastQuery) search(lastQuery)
} }

View file

@ -38,7 +38,7 @@ import org.oxycblt.auxio.settings.ui.IntListPreferenceDialog
import org.oxycblt.auxio.settings.ui.PreferenceHeaderItemDecoration import org.oxycblt.auxio.settings.ui.PreferenceHeaderItemDecoration
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
import org.oxycblt.auxio.util.systemBarInsetsCompat import org.oxycblt.auxio.util.systemBarInsetsCompat
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Shared [PreferenceFragmentCompat] used across all preference screens. * Shared [PreferenceFragmentCompat] used across all preference screens.
@ -82,7 +82,7 @@ abstract class BasePreferenceFragment(@XmlRes private val screen: Int) :
preferenceManager.onDisplayPreferenceDialogListener = this preferenceManager.onDisplayPreferenceDialogListener = this
preferenceScreen.children.forEach(::setupPreference) preferenceScreen.children.forEach(::setupPreference)
T.d("Fragment created") L.d("Fragment created")
} }
override fun onCreateRecyclerView( override fun onCreateRecyclerView(

View file

@ -30,7 +30,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* The [PreferenceFragmentCompat] that displays the root settings list. * The [PreferenceFragmentCompat] that displays the root settings list.
@ -62,21 +62,21 @@ class RootPreferenceFragment : BasePreferenceFragment(R.xml.preferences_root) {
// do one. // do one.
when (preference.key) { when (preference.key) {
getString(R.string.set_key_ui) -> { getString(R.string.set_key_ui) -> {
T.d("Navigating to UI preferences") L.d("Navigating to UI preferences")
findNavController().navigateSafe(RootPreferenceFragmentDirections.uiPreferences()) findNavController().navigateSafe(RootPreferenceFragmentDirections.uiPreferences())
} }
getString(R.string.set_key_personalize) -> { getString(R.string.set_key_personalize) -> {
T.d("Navigating to personalization preferences") L.d("Navigating to personalization preferences")
findNavController() findNavController()
.navigateSafe(RootPreferenceFragmentDirections.personalizePreferences()) .navigateSafe(RootPreferenceFragmentDirections.personalizePreferences())
} }
getString(R.string.set_key_music) -> { getString(R.string.set_key_music) -> {
T.d("Navigating to music preferences") L.d("Navigating to music preferences")
findNavController() findNavController()
.navigateSafe(RootPreferenceFragmentDirections.musicPreferences()) .navigateSafe(RootPreferenceFragmentDirections.musicPreferences())
} }
getString(R.string.set_key_audio) -> { getString(R.string.set_key_audio) -> {
T.d("Navigating to audio preferences") L.d("Navigating to audio preferences")
findNavController().navigateSafe(RootPreferenceFragmentDirections.audioPeferences()) findNavController().navigateSafe(RootPreferenceFragmentDirections.audioPeferences())
} }
getString(R.string.set_key_reindex) -> musicModel.refresh() getString(R.string.set_key_reindex) -> musicModel.refresh()

View file

@ -23,7 +23,7 @@ import android.content.SharedPreferences
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Abstract user configuration information. This interface has no functionality whatsoever. Concrete * Abstract user configuration information. This interface has no functionality whatsoever. Concrete
@ -73,19 +73,19 @@ interface Settings<L> {
override fun registerListener(listener: L) { override fun registerListener(listener: L) {
if (this.listener == null) { if (this.listener == null) {
// Registering a listener when it was null prior, attach the callback. // Registering a listener when it was null prior, attach the callback.
T.d("Registering shared preference listener") L.d("Registering shared preference listener")
sharedPreferences.registerOnSharedPreferenceChangeListener(this) sharedPreferences.registerOnSharedPreferenceChangeListener(this)
} }
T.d("Registering listener $listener") L.d("Registering listener $listener")
this.listener = listener this.listener = listener
} }
override fun unregisterListener(listener: L) { override fun unregisterListener(listener: L) {
if (this.listener !== listener) { if (this.listener !== listener) {
T.w("Given listener was not the current listener.") L.w("Given listener was not the current listener.")
return return
} }
T.d("Unregistering listener $listener") L.d("Unregistering listener $listener")
this.listener = null this.listener = null
// No longer have a listener, detach from the preferences instance. // No longer have a listener, detach from the preferences instance.
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
@ -96,7 +96,7 @@ interface Settings<L> {
key: String? key: String?
) { ) {
// FIXME: Settings initialization firing the listener. // FIXME: Settings initialization firing the listener.
T.d("Dispatching settings change $key") L.d("Dispatching settings change $key")
onSettingChanged(unlikelyToBeNull(key), unlikelyToBeNull(listener)) onSettingChanged(unlikelyToBeNull(key), unlikelyToBeNull(listener))
} }

View file

@ -23,7 +23,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.BasePreferenceFragment import org.oxycblt.auxio.settings.BasePreferenceFragment
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Audio settings interface. * Audio settings interface.
@ -34,7 +34,7 @@ class AudioPreferenceFragment : BasePreferenceFragment(R.xml.preferences_audio)
override fun onOpenDialogPreference(preference: WrappedDialogPreference) { override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
if (preference.key == getString(R.string.set_key_pre_amp)) { if (preference.key == getString(R.string.set_key_pre_amp)) {
T.d("Navigating to pre-amp dialog") L.d("Navigating to pre-amp dialog")
findNavController().navigateSafe(AudioPreferenceFragmentDirections.preAmpSettings()) findNavController().navigateSafe(AudioPreferenceFragmentDirections.preAmpSettings())
} }
} }

View file

@ -27,7 +27,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.BasePreferenceFragment import org.oxycblt.auxio.settings.BasePreferenceFragment
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* "Content" settings. * "Content" settings.
@ -40,7 +40,7 @@ class MusicPreferenceFragment : BasePreferenceFragment(R.xml.preferences_music)
override fun onOpenDialogPreference(preference: WrappedDialogPreference) { override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
if (preference.key == getString(R.string.set_key_separators)) { if (preference.key == getString(R.string.set_key_separators)) {
T.d("Navigating to separator dialog") L.d("Navigating to separator dialog")
findNavController().navigateSafe(MusicPreferenceFragmentDirections.separatorsSettings()) findNavController().navigateSafe(MusicPreferenceFragmentDirections.separatorsSettings())
} }
} }
@ -48,10 +48,10 @@ class MusicPreferenceFragment : BasePreferenceFragment(R.xml.preferences_music)
override fun onSetupPreference(preference: Preference) { override fun onSetupPreference(preference: Preference) {
if (preference.key == getString(R.string.set_key_cover_mode) || if (preference.key == getString(R.string.set_key_cover_mode) ||
preference.key == getString(R.string.set_key_square_covers)) { preference.key == getString(R.string.set_key_square_covers)) {
T.d("Configuring cover mode setting") L.d("Configuring cover mode setting")
preference.onPreferenceChangeListener = preference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, _ -> Preference.OnPreferenceChangeListener { _, _ ->
T.d("Cover mode changed, resetting image memory cache") L.d("Cover mode changed, resetting image memory cache")
imageLoader.memoryCache?.clear() imageLoader.memoryCache?.clear()
true true
} }

View file

@ -23,7 +23,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.BasePreferenceFragment import org.oxycblt.auxio.settings.BasePreferenceFragment
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Personalization settings interface. * Personalization settings interface.
@ -33,7 +33,7 @@ import timber.log.Timber as T
class PersonalizePreferenceFragment : BasePreferenceFragment(R.xml.preferences_personalize) { class PersonalizePreferenceFragment : BasePreferenceFragment(R.xml.preferences_personalize) {
override fun onOpenDialogPreference(preference: WrappedDialogPreference) { override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
if (preference.key == getString(R.string.set_key_home_tabs)) { if (preference.key == getString(R.string.set_key_home_tabs)) {
T.d("Navigating to home tab dialog") L.d("Navigating to home tab dialog")
findNavController().navigateSafe(PersonalizePreferenceFragmentDirections.tabSettings()) findNavController().navigateSafe(PersonalizePreferenceFragmentDirections.tabSettings())
} }
} }

View file

@ -29,7 +29,7 @@ import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
import org.oxycblt.auxio.ui.UISettings import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.util.isNight import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import timber.log.Timber as T import timber.log.Timber as L
/** /**
* Display preferences. * Display preferences.
@ -42,7 +42,7 @@ class UIPreferenceFragment : BasePreferenceFragment(R.xml.preferences_ui) {
override fun onOpenDialogPreference(preference: WrappedDialogPreference) { override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
if (preference.key == getString(R.string.set_key_accent)) { if (preference.key == getString(R.string.set_key_accent)) {
T.d("Navigating to accent dialog") L.d("Navigating to accent dialog")
findNavController().navigateSafe(UIPreferenceFragmentDirections.accentSettings()) findNavController().navigateSafe(UIPreferenceFragmentDirections.accentSettings())
} }
} }
@ -50,25 +50,25 @@ class UIPreferenceFragment : BasePreferenceFragment(R.xml.preferences_ui) {
override fun onSetupPreference(preference: Preference) { override fun onSetupPreference(preference: Preference) {
when (preference.key) { when (preference.key) {
getString(R.string.set_key_theme) -> { getString(R.string.set_key_theme) -> {
T.d("Configuring theme setting") L.d("Configuring theme setting")
preference.onPreferenceChangeListener = preference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, value -> Preference.OnPreferenceChangeListener { _, value ->
T.d("Theme changed, recreating") L.d("Theme changed, recreating")
AppCompatDelegate.setDefaultNightMode(value as Int) AppCompatDelegate.setDefaultNightMode(value as Int)
true true
} }
} }
getString(R.string.set_key_accent) -> { getString(R.string.set_key_accent) -> {
T.d("Configuring accent setting") L.d("Configuring accent setting")
preference.summary = getString(uiSettings.accent.name) preference.summary = getString(uiSettings.accent.name)
} }
getString(R.string.set_key_black_theme) -> { getString(R.string.set_key_black_theme) -> {
T.d("Configuring black theme setting") L.d("Configuring black theme setting")
preference.onPreferenceChangeListener = preference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, _ -> Preference.OnPreferenceChangeListener { _, _ ->
val activity = requireActivity() val activity = requireActivity()
if (activity.isNight) { if (activity.isNight) {
T.d("Black theme changed in night mode, recreating") L.d("Black theme changed in night mode, recreating")
activity.recreate() activity.recreate()
} }

Some files were not shown because too many files have changed in this diff Show more