From 4efbab8b05bb29788c8d25f2a6d5680e82f57698 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sat, 20 Feb 2021 13:47:06 -0700 Subject: [PATCH] Change file intent navigation structure When a file intent is given, navigate to PlaybackFragment directly. --- .../java/org/oxycblt/auxio/MainFragment.kt | 11 ++- .../auxio/music/processing/MusicLinker.kt | 18 ++--- .../oxycblt/auxio/playback/PlayPauseButton.kt | 6 +- .../auxio/playback/PlaybackFragment.kt | 20 ++--- .../oxycblt/auxio/playback/PlaybackUtils.kt | 19 +++++ .../auxio/playback/PlaybackViewModel.kt | 19 +---- .../auxio/playback/queue/QueueFragment.kt | 6 +- .../java/org/oxycblt/auxio/ui/ActionMenu.kt | 2 +- .../org/oxycblt/auxio/ui/InterfaceUtils.kt | 79 ++++++------------- 9 files changed, 86 insertions(+), 94 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/playback/PlaybackUtils.kt diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index f913fbf40..477e7aeb9 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -19,9 +19,9 @@ import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.playback.shouldHandleFileIntent import org.oxycblt.auxio.ui.Accent import org.oxycblt.auxio.ui.fixAnimInfoLeak -import org.oxycblt.auxio.ui.handleFileIntent import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isTablet import org.oxycblt.auxio.ui.toColor @@ -119,7 +119,14 @@ class MainFragment : Fragment() { override fun onResume() { super.onResume() - if (!handleFileIntent(playbackModel)) { + // File intents chain-navigate towards PlaybackFragment + // Not for any express purpose, I just prefer it this way. + if (shouldHandleFileIntent()) { + findNavController().navigate( + MainFragmentDirections.actionGoToPlayback() + ) + } else { + // If there is no file intent restore playback as usual playbackModel.restorePlaybackIfNeeded(requireContext()) } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLinker.kt b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLinker.kt index e7095089f..23b7e72fd 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLinker.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLinker.kt @@ -70,15 +70,15 @@ class MusicLinker( logD("Linking genres") /* - * Okay, I'm going to go on a bit of a tangent here, but why the hell do I have do this? - * In an ideal world I should just be able to write MediaStore.Media.Audio.GENRE in the - * original song projection and then have it fetch the genre from the database, but no, - * why would ANYONE do that? Instead, I have to manually PROJECT EACH GENRE, get their - * song ids, and then waste CPU cycles REPEATEDLY ITERATING through the songs list - * to LINK SONG WITH THEIR GENRE. I bet the google dev who built this busted system - * feels REALLY happy about the promotion they likely got from rushing out another - * android API that quickly rots from the basic act of existing, because now this quirk - * is immortalized and has to be replicated to be backwards compatible! Thanks for nothing! + * Okay, I'm going to go on a bit of a tangent here because this bit of code infuriates me. + * + * In an ideal world I should just be able to write MediaStore.Media.Audio.GENRE + * in the original song projection and then have it fetch the genre from the database, but + * no, why would ANYONE do that? Instead, I have to manually iterate through each genre, get + * A LIST OF SONGS FROM THEM, and then waste CPU cycles REPEATEDLY ITERATING through the + * songs list to LINK EACH SONG WITH THEIR GENRE. Why is it this way? Nobody knows! Now this + * quirk is immortalized and has to be replicated in all future iterations of the API! + * Yay! I hate this platform so much. */ genres.forEach { genre -> val songCursor = resolver.query( diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt index 8efb47da7..a5a772c05 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt @@ -8,7 +8,7 @@ import android.util.AttributeSet import androidx.annotation.RequiresApi import androidx.appcompat.widget.AppCompatImageButton import org.oxycblt.auxio.R -import org.oxycblt.auxio.ui.getAnimatedDrawable +import org.oxycblt.auxio.ui.toAnimDrawable /** * Custom [AppCompatImageButton] that handles the animated play/pause icons. @@ -18,8 +18,8 @@ class PlayPauseButton @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = -1 ) : AppCompatImageButton(context, attrs, defStyleAttr) { - private val iconPauseToPlay = context.getAnimatedDrawable(R.drawable.ic_pause_to_play) - private val iconPlayToPause = context.getAnimatedDrawable(R.drawable.ic_play_to_pause) + private val iconPauseToPlay = R.drawable.ic_pause_to_play.toAnimDrawable(context) + private val iconPlayToPause = R.drawable.ic_play_to_pause.toAnimDrawable(context) init { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index c495d88cb..beaff272f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -17,8 +17,8 @@ import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.logD import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.ui.Accent -import org.oxycblt.auxio.ui.handleFileIntent import org.oxycblt.auxio.ui.memberBinding +import org.oxycblt.auxio.ui.toDrawable import org.oxycblt.auxio.ui.toStateList /** @@ -53,13 +53,8 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { val normalTextColor = binding.playbackDurationCurrent.currentTextColor // Can't set the tint of a MenuItem below Android 8, so use icons instead. - val iconQueueActive = ContextCompat.getDrawable( - requireContext(), R.drawable.ic_queue - ) - - val iconQueueInactive = ContextCompat.getDrawable( - requireContext(), R.drawable.ic_queue_inactive - ) + val iconQueueActive = R.drawable.ic_queue.toDrawable(requireContext()) + val iconQueueInactive = R.drawable.ic_queue_inactive.toDrawable(requireContext()) val queueMenuItem: MenuItem @@ -202,7 +197,14 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { playbackModel.enableAnimation() } - handleFileIntent(playbackModel) + if (shouldHandleFileIntent()) { + val intent = requireActivity().intent + + // Ensure that this wont fire again by putting a boolean extra + intent.putExtra(PlaybackUtils.KEY_INTENT_FIRED, true) + + playbackModel.playWithIntent(intent, requireContext()) + } } // --- SEEK CALLBACKS --- diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackUtils.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackUtils.kt new file mode 100644 index 000000000..24df2f900 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackUtils.kt @@ -0,0 +1,19 @@ +package org.oxycblt.auxio.playback + +import android.content.Intent +import androidx.fragment.app.Fragment + +object PlaybackUtils { + const val KEY_INTENT_FIRED = "KEY_FILE_INTENT_FIRED" +} + +/** + * Check if the current activity intent is a file intent and needs to be dealt with. + */ +fun Fragment.shouldHandleFileIntent(): Boolean { + val intent = requireActivity().intent + + return intent != null && + intent.action == Intent.ACTION_VIEW && + !intent.getBooleanExtra(PlaybackUtils.KEY_INTENT_FIRED, false) +} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index d2f73c167..89b6acc22 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -160,18 +160,11 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { /** Play a song using an intent */ fun playWithIntent(intent: Intent, context: Context) { - val fired = intent.getBooleanExtra(KEY_INTENT_PLAY_FIRED, false) + val uri = intent.data ?: return - if (!fired) { - // Make sure any intents that are passed here never fire again - intent.putExtra(KEY_INTENT_PLAY_FIRED, true) - - val uri = intent.data ?: return - - viewModelScope.launch { - musicStore.getSongForUri(uri, context.contentResolver)?.let { song -> - playSong(song) - } + viewModelScope.launch { + musicStore.getSongForUri(uri, context.contentResolver)?.let { song -> + playSong(song) } } } @@ -427,8 +420,4 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { override fun onInUserQueueUpdate(isInUserQueue: Boolean) { mIsInUserQueue.value = isInUserQueue } - - companion object { - private const val KEY_INTENT_PLAY_FIRED = "KEY_PLAY_DONE" - } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt index 3187b70d4..f9d7a2aca 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt @@ -17,8 +17,8 @@ import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.playback.shouldHandleFileIntent import org.oxycblt.auxio.playback.state.PlaybackMode -import org.oxycblt.auxio.ui.handleFileIntent import org.oxycblt.auxio.ui.isEdgeOn import org.oxycblt.auxio.ui.isIrregularLandscape @@ -102,7 +102,9 @@ class QueueFragment : Fragment() { override fun onResume() { super.onResume() - handleFileIntent(playbackModel) + if (shouldHandleFileIntent()) { + findNavController().navigateUp() + } } /** diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt index 1f2d9b7a6..11126cd4f 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt @@ -25,7 +25,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode * @see ActionMenu */ fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_NONE) { - ActionMenu(requireCompatActivity(), anchor, data, flag).show() + ActionMenu(requireActivity() as AppCompatActivity, anchor, data, flag).show() } /** diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index c0341ddcf..44206de7b 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -2,12 +2,12 @@ package org.oxycblt.auxio.ui import android.app.Activity import android.content.Context -import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration import android.content.res.Resources import android.graphics.Point import android.graphics.drawable.AnimatedVectorDrawable +import android.graphics.drawable.Drawable import android.os.Build import android.util.DisplayMetrics import android.view.LayoutInflater @@ -27,7 +27,6 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import org.oxycblt.auxio.R import org.oxycblt.auxio.logE -import org.oxycblt.auxio.playback.PlaybackViewModel // --- VIEW CONFIGURATION --- @@ -65,6 +64,11 @@ fun MaterialButton.applyAccents(highlighted: Boolean) { // --- CONVENIENCE --- +/** + * Shortcut to get a [LayoutInflater] from a [Context] + */ +val Context.inflater: LayoutInflater get() = LayoutInflater.from(this) + /** * Convenience method for getting a plural. * @param pluralsRes Resource for the plural @@ -75,40 +79,6 @@ fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String { return resources.getQuantityString(pluralsRes, value, value) } -/** - * Shortcut to get a [LayoutInflater] from a [Context] - */ -val Context.inflater: LayoutInflater get() = LayoutInflater.from(this) - -/** - * Shortcut to get an [AnimatedVectorDrawable] from a [Context] - */ -fun Context.getAnimatedDrawable(@DrawableRes drawableRes: Int): AnimatedVectorDrawable { - return ContextCompat.getDrawable(this, drawableRes) as AnimatedVectorDrawable -} - -/** - * Create a [Toast] from a [String] - * @param context [Context] required to create the toast - */ -fun String.createToast(context: Context) { - Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show() -} - -/** - * Ensure that a not-null [AppCompatActivity] will be returned. - * @throws IllegalStateException When there is no [AppCompatActivity] or if the activity is null - */ -fun Fragment.requireCompatActivity(): AppCompatActivity { - val activity = requireActivity() - - if (activity is AppCompatActivity) { - return activity - } else { - error("Required AppCompatActivity, got ${activity::class.simpleName} instead.") - } -} - /** * Resolve a color. * @param context [Context] required @@ -132,7 +102,26 @@ fun Int.toColor(context: Context): Int { * @return The resolved color as a [ColorStateList] * @see toColor */ -fun Int.toStateList(context: Context): ColorStateList = ColorStateList.valueOf(toColor(context)) +fun Int.toStateList(context: Context) = ColorStateList.valueOf(toColor(context)) + +/** + * Resolve a drawable resource into a [Drawable] + */ +fun Int.toDrawable(context: Context) = ContextCompat.getDrawable(context, this) + +/** + * Resolve a drawable resource into an [AnimatedVectorDrawable] + * @see toDrawable + */ +fun Int.toAnimDrawable(context: Context) = toDrawable(context) as AnimatedVectorDrawable + +/** + * Create a [Toast] from a [String] + * @param context [Context] required to create the toast + */ +fun String.createToast(context: Context) { + Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show() +} // --- CONFIGURATION --- @@ -243,19 +232,3 @@ fun Fragment.fixAnimInfoLeak() { logE("mAnimationInfo leak fix failed.") } } - -/** - * Shortcut for handling a file intent. - * @return True if the file intent was pushed to [playbackModel], false if not - */ -fun Fragment.handleFileIntent(playbackModel: PlaybackViewModel): Boolean { - val intent = requireActivity().intent - - if (intent != null && intent.action == Intent.ACTION_VIEW) { - playbackModel.playWithIntent(intent, requireContext()) - - return true - } - - return false -}