Change file intent navigation structure

When a file intent is given, navigate to PlaybackFragment directly.
This commit is contained in:
OxygenCobalt 2021-02-20 13:47:06 -07:00
parent cca80c65eb
commit 4efbab8b05
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 86 additions and 94 deletions

View file

@ -19,9 +19,9 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.shouldHandleFileIntent
import org.oxycblt.auxio.ui.Accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.fixAnimInfoLeak import org.oxycblt.auxio.ui.fixAnimInfoLeak
import org.oxycblt.auxio.ui.handleFileIntent
import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.isTablet import org.oxycblt.auxio.ui.isTablet
import org.oxycblt.auxio.ui.toColor import org.oxycblt.auxio.ui.toColor
@ -119,7 +119,14 @@ class MainFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.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()) playbackModel.restorePlaybackIfNeeded(requireContext())
} }
} }

View file

@ -70,15 +70,15 @@ class MusicLinker(
logD("Linking genres") 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? * 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, * In an ideal world I should just be able to write MediaStore.Media.Audio.GENRE
* why would ANYONE do that? Instead, I have to manually PROJECT EACH GENRE, get their * in the original song projection and then have it fetch the genre from the database, but
* song ids, and then waste CPU cycles REPEATEDLY ITERATING through the songs list * no, why would ANYONE do that? Instead, I have to manually iterate through each genre, get
* to LINK SONG WITH THEIR GENRE. I bet the google dev who built this busted system * A LIST OF SONGS FROM THEM, and then waste CPU cycles REPEATEDLY ITERATING through the
* feels REALLY happy about the promotion they likely got from rushing out another * songs list to LINK EACH SONG WITH THEIR GENRE. Why is it this way? Nobody knows! Now this
* android API that quickly rots from the basic act of existing, because now this quirk * quirk is immortalized and has to be replicated in all future iterations of the API!
* is immortalized and has to be replicated to be backwards compatible! Thanks for nothing! * Yay! I hate this platform so much.
*/ */
genres.forEach { genre -> genres.forEach { genre ->
val songCursor = resolver.query( val songCursor = resolver.query(

View file

@ -8,7 +8,7 @@ import android.util.AttributeSet
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.widget.AppCompatImageButton import androidx.appcompat.widget.AppCompatImageButton
import org.oxycblt.auxio.R 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. * Custom [AppCompatImageButton] that handles the animated play/pause icons.
@ -18,8 +18,8 @@ class PlayPauseButton @JvmOverloads constructor(
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = -1 defStyleAttr: Int = -1
) : AppCompatImageButton(context, attrs, defStyleAttr) { ) : AppCompatImageButton(context, attrs, defStyleAttr) {
private val iconPauseToPlay = context.getAnimatedDrawable(R.drawable.ic_pause_to_play) private val iconPauseToPlay = R.drawable.ic_pause_to_play.toAnimDrawable(context)
private val iconPlayToPause = context.getAnimatedDrawable(R.drawable.ic_play_to_pause) private val iconPlayToPause = R.drawable.ic_play_to_pause.toAnimDrawable(context)
init { init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

View file

@ -17,8 +17,8 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.ui.Accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.handleFileIntent
import org.oxycblt.auxio.ui.memberBinding import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.ui.toDrawable
import org.oxycblt.auxio.ui.toStateList import org.oxycblt.auxio.ui.toStateList
/** /**
@ -53,13 +53,8 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
val normalTextColor = binding.playbackDurationCurrent.currentTextColor val normalTextColor = binding.playbackDurationCurrent.currentTextColor
// Can't set the tint of a MenuItem below Android 8, so use icons instead. // Can't set the tint of a MenuItem below Android 8, so use icons instead.
val iconQueueActive = ContextCompat.getDrawable( val iconQueueActive = R.drawable.ic_queue.toDrawable(requireContext())
requireContext(), R.drawable.ic_queue val iconQueueInactive = R.drawable.ic_queue_inactive.toDrawable(requireContext())
)
val iconQueueInactive = ContextCompat.getDrawable(
requireContext(), R.drawable.ic_queue_inactive
)
val queueMenuItem: MenuItem val queueMenuItem: MenuItem
@ -202,7 +197,14 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
playbackModel.enableAnimation() 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 --- // --- SEEK CALLBACKS ---

View file

@ -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)
}

View file

@ -160,12 +160,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
/** Play a song using an intent */ /** Play a song using an intent */
fun playWithIntent(intent: Intent, context: Context) { fun playWithIntent(intent: Intent, context: Context) {
val fired = intent.getBooleanExtra(KEY_INTENT_PLAY_FIRED, false)
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 val uri = intent.data ?: return
viewModelScope.launch { viewModelScope.launch {
@ -174,7 +168,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
} }
} }
} }
}
// --- POSITION FUNCTIONS --- // --- POSITION FUNCTIONS ---
@ -427,8 +420,4 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
override fun onInUserQueueUpdate(isInUserQueue: Boolean) { override fun onInUserQueueUpdate(isInUserQueue: Boolean) {
mIsInUserQueue.value = isInUserQueue mIsInUserQueue.value = isInUserQueue
} }
companion object {
private const val KEY_INTENT_PLAY_FIRED = "KEY_PLAY_DONE"
}
} }

View file

@ -17,8 +17,8 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.shouldHandleFileIntent
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.handleFileIntent
import org.oxycblt.auxio.ui.isEdgeOn import org.oxycblt.auxio.ui.isEdgeOn
import org.oxycblt.auxio.ui.isIrregularLandscape import org.oxycblt.auxio.ui.isIrregularLandscape
@ -102,7 +102,9 @@ class QueueFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
handleFileIntent(playbackModel) if (shouldHandleFileIntent()) {
findNavController().navigateUp()
}
} }
/** /**

View file

@ -25,7 +25,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
* @see ActionMenu * @see ActionMenu
*/ */
fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_NONE) { 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()
} }
/** /**

View file

@ -2,12 +2,12 @@ package org.oxycblt.auxio.ui
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Point import android.graphics.Point
import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.LayoutInflater import android.view.LayoutInflater
@ -27,7 +27,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.logE import org.oxycblt.auxio.logE
import org.oxycblt.auxio.playback.PlaybackViewModel
// --- VIEW CONFIGURATION --- // --- VIEW CONFIGURATION ---
@ -65,6 +64,11 @@ fun MaterialButton.applyAccents(highlighted: Boolean) {
// --- CONVENIENCE --- // --- CONVENIENCE ---
/**
* Shortcut to get a [LayoutInflater] from a [Context]
*/
val Context.inflater: LayoutInflater get() = LayoutInflater.from(this)
/** /**
* Convenience method for getting a plural. * Convenience method for getting a plural.
* @param pluralsRes Resource for the 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) 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. * Resolve a color.
* @param context [Context] required * @param context [Context] required
@ -132,7 +102,26 @@ fun Int.toColor(context: Context): Int {
* @return The resolved color as a [ColorStateList] * @return The resolved color as a [ColorStateList]
* @see toColor * @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 --- // --- CONFIGURATION ---
@ -243,19 +232,3 @@ fun Fragment.fixAnimInfoLeak() {
logE("mAnimationInfo leak fix failed.") 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
}