Change file intent navigation structure
When a file intent is given, navigate to PlaybackFragment directly.
This commit is contained in:
parent
cca80c65eb
commit
4efbab8b05
9 changed files with 86 additions and 94 deletions
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ---
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue