From 4f8fc8008ceb347a91b6020d9b35a5ce7265a545 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sun, 3 Jul 2022 14:47:10 -0600 Subject: [PATCH] music: rework loading ui flow Rework the UI flow for music loading to be more coherent with runtime rescanning. The loading progress is now shown as a card on the bottom of the screen. This way, app use is not completely crippled when the app has to rescan the music library, albeit the shuffle button still has to be disabled during this period. --- .../org/oxycblt/auxio/home/HomeFragment.kt | 46 ++++----- .../java/org/oxycblt/auxio/music/Indexer.kt | 11 ++- .../org/oxycblt/auxio/music/IndexerService.kt | 2 - app/src/main/res/layout/fragment_home.xml | 96 +++++++++++-------- 4 files changed, 80 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt index 2775306d5..4673c2f8b 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -98,7 +98,7 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI binding.homeToolbar.alpha = 1f - (abs(offset.toFloat()) / (range.toFloat() / 2)) - binding.homeContent.updatePadding( + binding.homePager.updatePadding( bottom = binding.homeAppbar.totalScrollRange + offset) } } @@ -107,7 +107,7 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI updateTabConfiguration() - binding.homeIndexingContainer.setOnApplyWindowInsetsListener { view, insets -> + binding.homeIndexingWrapper.setOnApplyWindowInsetsListener { view, insets -> view.updatePadding(bottom = insets.systemBarInsetsCompat.bottom) insets } @@ -141,7 +141,7 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI // --- VIEWMODEL SETUP --- - collect(homeModel.isFastScrolling, ::updateFastScrolling) + collect(homeModel.isFastScrolling) { updateFab() } collect(homeModel.recreateTabs, ::handleRecreateTabs) collectImmediately(homeModel.currentTab, ::updateCurrentTab) collectImmediately(indexerModel.state, ::handleIndexerState) @@ -190,23 +190,6 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI return true } - private fun updateFastScrolling(isFastScrolling: Boolean) { - val binding = requireBinding() - - // Make sure an update here doesn't mess up the FAB state when it comes to the - // loader response. - val state = indexerModel.state.value - if (!(state is Indexer.State.Complete && state.response is Indexer.Response.Ok)) { - return - } - - if (isFastScrolling) { - binding.homeFab.hide() - } else { - binding.homeFab.show() - } - } - private fun updateCurrentTab(tab: DisplayMode) { // Make sure that we update the scrolling view and allowed menu items whenever // the tab changes. @@ -274,27 +257,23 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI private fun handleIndexerState(state: Indexer.State?) { val binding = requireBinding() - when (state) { is Indexer.State.Complete -> handleIndexerResponse(binding, state.response) is Indexer.State.Indexing -> handleIndexingState(binding, state.indexing) null -> { logD("Indexer is in indeterminate state") - binding.homeFab.hide() binding.homeIndexingContainer.visibility = View.INVISIBLE - binding.homePager.visibility = View.INVISIBLE } } + + updateFab() } private fun handleIndexerResponse(binding: FragmentHomeBinding, response: Indexer.Response) { if (response is Indexer.Response.Ok) { binding.homeFab.show() binding.homeIndexingContainer.visibility = View.INVISIBLE - binding.homePager.visibility = View.VISIBLE } else { - binding.homeFab.hide() - binding.homePager.visibility = View.INVISIBLE binding.homeIndexingContainer.visibility = View.VISIBLE logD("Received non-ok response $response") @@ -336,8 +315,7 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI } private fun handleIndexingState(binding: FragmentHomeBinding, indexing: Indexer.Indexing) { - binding.homeFab.hide() - binding.homePager.visibility = View.INVISIBLE + updateFab() binding.homeIndexingContainer.visibility = View.VISIBLE binding.homeIndexingProgress.visibility = View.VISIBLE binding.homeIndexingAction.visibility = View.INVISIBLE @@ -359,6 +337,18 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI } } + private fun updateFab() { + val binding = requireBinding() + val fastScrolling = homeModel.isFastScrolling.value + val state = indexerModel.state.value + if (fastScrolling || + !(state is Indexer.State.Complete && state.response is Indexer.Response.Ok)) { + binding.homeFab.hide() + } else { + binding.homeFab.show() + } + } + private fun handleNavigation(item: Music?) { // Note: You will want to add a post call to this if you want to re-introduce a collapsing // toolbar. diff --git a/app/src/main/java/org/oxycblt/auxio/music/Indexer.kt b/app/src/main/java/org/oxycblt/auxio/music/Indexer.kt index 2535ee0c9..037f55935 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Indexer.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Indexer.kt @@ -23,6 +23,7 @@ import android.content.pm.PackageManager import android.database.Cursor import android.os.Build import androidx.core.content.ContextCompat +import kotlin.coroutines.cancellation.CancellationException import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.music.backend.Api21MediaStoreBackend import org.oxycblt.auxio.music.backend.Api29MediaStoreBackend @@ -121,7 +122,7 @@ class Indexer { this.callback = null } - fun index(context: Context) { + suspend fun index(context: Context) { val generation = synchronized(this) { ++currentGeneration } val notGranted = @@ -181,7 +182,9 @@ class Indexer { @Synchronized private fun emitIndexing(indexing: Indexing?, generation: Long) { if (currentGeneration != generation) { - return + // Not the running task anymore, cancel this co-routine + // We do this instead of using yield since it is *far* cheaper. + throw CancellationException() } indexingState = indexing @@ -198,7 +201,9 @@ class Indexer { @Synchronized private fun emitCompletion(response: Response, generation: Long) { if (currentGeneration != generation) { - return + // Not the running task anymore, cancel this co-routine + // We do this instead of using yield since it is *far* cheaper. + throw CancellationException() } lastResponse = response diff --git a/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt b/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt index 8641f4b59..21abe20d6 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt @@ -46,8 +46,6 @@ import org.oxycblt.auxio.util.logD * @author OxygenCobalt * * TODO: Add file observing - * - * TODO: Rework UI flow once again */ class IndexerService : Service(), Indexer.Controller, Settings.Callback { private val indexer = Indexer.getInstance() diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index d1c589bd8..5128d9f0e 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -29,60 +29,72 @@ - + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" + tools:layout="@layout/fragment_home_list" /> - + + + android:layout_height="wrap_content" + android:layout_margin="@dimen/spacing_medium"> - + android:animateLayoutChanges="true"> - + -