home: temporarily re-add no music case
I actually have no idea how to go about decoupling the other music loading errors from the "No Music" case without a very jarring user experience, so I'm not doing it for now.
This commit is contained in:
parent
9ab729a069
commit
4db4b023d5
3 changed files with 55 additions and 32 deletions
|
@ -338,28 +338,43 @@ class HomeFragment :
|
|||
val throwable = unlikelyToBeNull(result.exceptionOrNull())
|
||||
binding.homeIndexingContainer.visibility = View.VISIBLE
|
||||
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||
if (throwable is Indexer.NoPermissionException) {
|
||||
logD("Updating UI to permission request state")
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_perms)
|
||||
// Configure the action to act as a permission launcher.
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = context.getString(R.string.lbl_grant)
|
||||
setOnClickListener {
|
||||
requireNotNull(storagePermissionLauncher) {
|
||||
"Permission launcher was not available"
|
||||
}
|
||||
.launch(Indexer.PERMISSION_READ_AUDIO)
|
||||
when (throwable) {
|
||||
is Indexer.NoPermissionException -> {
|
||||
logD("Updating UI to permission request state")
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_perms)
|
||||
// Configure the action to act as a permission launcher.
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = context.getString(R.string.lbl_grant)
|
||||
setOnClickListener {
|
||||
requireNotNull(storagePermissionLauncher) {
|
||||
"Permission launcher was not available"
|
||||
}
|
||||
.launch(Indexer.PERMISSION_READ_AUDIO)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logD("Updating UI to error state")
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_index_failed)
|
||||
// Configure the action to act as a reload trigger.
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.refresh() }
|
||||
is Indexer.NoMusicException -> {
|
||||
logD("Updating UI to no music state")
|
||||
// TODO: Rework how empty libraries are treated to feel less jarring if
|
||||
// there was a previously loaded library
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_music)
|
||||
// Configure the action to act as a reload trigger.
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.refresh() }
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
logD("Updating UI to error state")
|
||||
binding.homeIndexingStatus.text = context.getString(R.string.err_index_failed)
|
||||
// Configure the action to act as a reload trigger.
|
||||
binding.homeIndexingAction.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.rescan() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,10 +435,9 @@ class HomeFragment :
|
|||
val binding = requireBinding()
|
||||
if (binding.homeSelectionToolbar.updateSelectionAmount(selected.size) &&
|
||||
selected.isNotEmpty()) {
|
||||
// New selection started, show the AppBarLayout to indicate the new state.
|
||||
logD("Significant selection occurred, expanding AppBar")
|
||||
// Significant enough change where we want to expand the RecyclerView
|
||||
binding.homeAppbar.expandWithRecycler(
|
||||
binding.homePager.findViewById(getTabRecyclerId(homeModel.currentTabMode.value)))
|
||||
binding.homeAppbar.expandWithScrollingRecycler()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -197,7 +197,9 @@ class Indexer private constructor() {
|
|||
* @param context [Context] required to load music.
|
||||
* @param withCache Whether to use the cache or not when loading. If false, the cache will still
|
||||
* be written, but no cache entries will be loaded into the new library.
|
||||
* @return A newly-loaded [MusicStore.Library]. May be empty.
|
||||
* @return A newly-loaded [MusicStore.Library].
|
||||
* @throws NoPermissionException If [PERMISSION_READ_AUDIO] was not granted.
|
||||
* @throws NoMusicException If no music was found on the device.
|
||||
*/
|
||||
private suspend fun indexImpl(context: Context, withCache: Boolean): MusicStore.Library {
|
||||
if (ContextCompat.checkSelfPermission(context, PERMISSION_READ_AUDIO) ==
|
||||
|
@ -227,7 +229,8 @@ class Indexer private constructor() {
|
|||
|
||||
val metadataExtractor = MetadataExtractor(context, mediaStoreExtractor)
|
||||
|
||||
val songs = buildSongs(metadataExtractor, Settings(context))
|
||||
val songs =
|
||||
buildSongs(metadataExtractor, Settings(context)).ifEmpty { throw NoMusicException() }
|
||||
// Build the rest of the music library from the song list. This is much more powerful
|
||||
// and reliable compared to using MediaStore to obtain grouping information.
|
||||
val buildStart = System.currentTimeMillis()
|
||||
|
@ -380,7 +383,7 @@ class Indexer private constructor() {
|
|||
* Emit a new [State.Complete] state. This can be used to signal the completion of the music
|
||||
* loading process to external code. Will check if the callee has not been canceled and thus has
|
||||
* the ability to emit a new state
|
||||
* @param result The new [Response] to emit, representing the outcome of the music loading
|
||||
* @param result The new [Result] to emit, representing the outcome of the music loading
|
||||
* process.
|
||||
*/
|
||||
private suspend fun emitCompletion(result: Result<MusicStore.Library>) {
|
||||
|
@ -442,6 +445,12 @@ class Indexer private constructor() {
|
|||
get() = "Not granted permissions to load music library"
|
||||
}
|
||||
|
||||
/** Thrown when no music was found on the device. */
|
||||
class NoMusicException : Exception() {
|
||||
override val message: String
|
||||
get() = "Unable to find any music"
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener for rapid-fire changes in the music loading state.
|
||||
*
|
||||
|
|
|
@ -68,14 +68,14 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
}
|
||||
|
||||
/**
|
||||
* Expand this [AppBarLayout] with respect to the given [RecyclerView], preventing it from
|
||||
* jumping around.
|
||||
* @param recycler [RecyclerView] to expand with, or null if one is currently unavailable.
|
||||
* Expand this [AppBarLayout] with respect to the current [RecyclerView] at
|
||||
* [liftOnScrollTargetViewId], preventing it from jumping around.
|
||||
*/
|
||||
fun expandWithRecycler(recycler: RecyclerView?) {
|
||||
// TODO: Is it possible to use liftOnScrollTargetViewId to avoid the RecyclerView arg?
|
||||
fun expandWithScrollingRecycler() {
|
||||
setExpanded(true)
|
||||
recycler?.let { addOnOffsetChangedListener(ExpansionHackListener(it)) }
|
||||
(findScrollingChild() as? RecyclerView)?.let {
|
||||
addOnOffsetChangedListener(ExpansionHackListener(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
|
|
Loading…
Reference in a new issue