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())
|
val throwable = unlikelyToBeNull(result.exceptionOrNull())
|
||||||
binding.homeIndexingContainer.visibility = View.VISIBLE
|
binding.homeIndexingContainer.visibility = View.VISIBLE
|
||||||
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
binding.homeIndexingProgress.visibility = View.INVISIBLE
|
||||||
if (throwable is Indexer.NoPermissionException) {
|
when (throwable) {
|
||||||
logD("Updating UI to permission request state")
|
is Indexer.NoPermissionException -> {
|
||||||
binding.homeIndexingStatus.text = context.getString(R.string.err_no_perms)
|
logD("Updating UI to permission request state")
|
||||||
// Configure the action to act as a permission launcher.
|
binding.homeIndexingStatus.text = context.getString(R.string.err_no_perms)
|
||||||
binding.homeIndexingAction.apply {
|
// Configure the action to act as a permission launcher.
|
||||||
visibility = View.VISIBLE
|
binding.homeIndexingAction.apply {
|
||||||
text = context.getString(R.string.lbl_grant)
|
visibility = View.VISIBLE
|
||||||
setOnClickListener {
|
text = context.getString(R.string.lbl_grant)
|
||||||
requireNotNull(storagePermissionLauncher) {
|
setOnClickListener {
|
||||||
"Permission launcher was not available"
|
requireNotNull(storagePermissionLauncher) {
|
||||||
}
|
"Permission launcher was not available"
|
||||||
.launch(Indexer.PERMISSION_READ_AUDIO)
|
}
|
||||||
|
.launch(Indexer.PERMISSION_READ_AUDIO)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
is Indexer.NoMusicException -> {
|
||||||
logD("Updating UI to error state")
|
logD("Updating UI to no music state")
|
||||||
binding.homeIndexingStatus.text = context.getString(R.string.err_index_failed)
|
// TODO: Rework how empty libraries are treated to feel less jarring if
|
||||||
// Configure the action to act as a reload trigger.
|
// there was a previously loaded library
|
||||||
binding.homeIndexingAction.apply {
|
binding.homeIndexingStatus.text = context.getString(R.string.err_no_music)
|
||||||
visibility = View.VISIBLE
|
// Configure the action to act as a reload trigger.
|
||||||
text = context.getString(R.string.lbl_retry)
|
binding.homeIndexingAction.apply {
|
||||||
setOnClickListener { musicModel.refresh() }
|
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()
|
val binding = requireBinding()
|
||||||
if (binding.homeSelectionToolbar.updateSelectionAmount(selected.size) &&
|
if (binding.homeSelectionToolbar.updateSelectionAmount(selected.size) &&
|
||||||
selected.isNotEmpty()) {
|
selected.isNotEmpty()) {
|
||||||
|
// New selection started, show the AppBarLayout to indicate the new state.
|
||||||
logD("Significant selection occurred, expanding AppBar")
|
logD("Significant selection occurred, expanding AppBar")
|
||||||
// Significant enough change where we want to expand the RecyclerView
|
binding.homeAppbar.expandWithScrollingRecycler()
|
||||||
binding.homeAppbar.expandWithRecycler(
|
|
||||||
binding.homePager.findViewById(getTabRecyclerId(homeModel.currentTabMode.value)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,9 @@ class Indexer private constructor() {
|
||||||
* @param context [Context] required to load music.
|
* @param context [Context] required to load music.
|
||||||
* @param withCache Whether to use the cache or not when loading. If false, the cache will still
|
* @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.
|
* 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 {
|
private suspend fun indexImpl(context: Context, withCache: Boolean): MusicStore.Library {
|
||||||
if (ContextCompat.checkSelfPermission(context, PERMISSION_READ_AUDIO) ==
|
if (ContextCompat.checkSelfPermission(context, PERMISSION_READ_AUDIO) ==
|
||||||
|
@ -227,7 +229,8 @@ class Indexer private constructor() {
|
||||||
|
|
||||||
val metadataExtractor = MetadataExtractor(context, mediaStoreExtractor)
|
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
|
// 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.
|
// and reliable compared to using MediaStore to obtain grouping information.
|
||||||
val buildStart = System.currentTimeMillis()
|
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
|
* 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
|
* loading process to external code. Will check if the callee has not been canceled and thus has
|
||||||
* the ability to emit a new state
|
* 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.
|
* process.
|
||||||
*/
|
*/
|
||||||
private suspend fun emitCompletion(result: Result<MusicStore.Library>) {
|
private suspend fun emitCompletion(result: Result<MusicStore.Library>) {
|
||||||
|
@ -442,6 +445,12 @@ class Indexer private constructor() {
|
||||||
get() = "Not granted permissions to load music library"
|
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.
|
* 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
|
* Expand this [AppBarLayout] with respect to the current [RecyclerView] at
|
||||||
* jumping around.
|
* [liftOnScrollTargetViewId], preventing it from jumping around.
|
||||||
* @param recycler [RecyclerView] to expand with, or null if one is currently unavailable.
|
|
||||||
*/
|
*/
|
||||||
fun expandWithRecycler(recycler: RecyclerView?) {
|
fun expandWithScrollingRecycler() {
|
||||||
// TODO: Is it possible to use liftOnScrollTargetViewId to avoid the RecyclerView arg?
|
|
||||||
setExpanded(true)
|
setExpanded(true)
|
||||||
recycler?.let { addOnOffsetChangedListener(ExpansionHackListener(it)) }
|
(findScrollingChild() as? RecyclerView)?.let {
|
||||||
|
addOnOffsetChangedListener(ExpansionHackListener(it))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
override fun onDetachedFromWindow() {
|
||||||
|
|
Loading…
Reference in a new issue