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.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())
}
}

View file

@ -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(

View file

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

View file

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

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 */
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
viewModelScope.launch {
@ -174,7 +168,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
}
}
}
}
// --- POSITION FUNCTIONS ---
@ -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"
}
}

View file

@ -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()
}
}
/**

View file

@ -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()
}
/**

View file

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