Action toolbar adjustments (#6353)

* Pair ActionToolbar with ActionMode

This makes ActionToolbar an activity object that can be configured in the
similar way as ActionMode

* Remove action toolbar workaround now that it stays in activity layout

5924

* Set status bar color when action mode is active

6256

* Adjust fab show timing after action mode finished

* Adjust action toolbar layout and animation

Default corner size and use bottom sheet animation

6069

* Adjust action toolbar layout on large screen

Right half of the screen
This commit is contained in:
Ivan Iskandar
2021-12-19 02:16:26 +07:00
committed by GitHub
parent afc80d6a7c
commit 2ed01af723
14 changed files with 318 additions and 226 deletions

View File

@@ -7,7 +7,6 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import com.bluelinelabs.conductor.ControllerChangeHandler
@@ -16,7 +15,6 @@ import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import com.tfcporciuncula.flow.Preference
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
@@ -35,6 +33,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.widget.ActionModeWithToolbar
import eu.kanade.tachiyomi.widget.EmptyView
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
import kotlinx.coroutines.flow.drop
@@ -55,7 +54,7 @@ class LibraryController(
) : SearchableNucleusController<LibraryControllerBinding, LibraryPresenter>(bundle),
RootController,
TabbedController,
ActionMode.Callback,
ActionModeWithToolbar.Callback,
ChangeMangaCategoriesDialog.Listener,
DeleteLibraryMangasDialog.Listener {
@@ -67,7 +66,7 @@ class LibraryController(
/**
* Action mode for selections.
*/
private var actionMode: ActionMode? = null
private var actionMode: ActionModeWithToolbar? = null
/**
* Currently selected mangas.
@@ -170,12 +169,6 @@ class LibraryController(
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.actionToolbar.applyInsetter {
type(navigationBars = true) {
margin(bottom = true, horizontal = true)
}
}
adapter = LibraryAdapter(this)
binding.libraryPager.adapter = adapter
binding.libraryPager.pageSelections()
@@ -233,7 +226,6 @@ class LibraryController(
override fun onDestroyView(view: View) {
destroyActionModeIfNeeded()
binding.actionToolbar.destroy()
adapter?.onDestroy()
adapter = null
settingsSheet = null
@@ -377,13 +369,10 @@ class LibraryController(
* Creates the action mode if it's not created already.
*/
fun createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
binding.actionToolbar.show(
actionMode!!,
R.menu.library_selection
) { onActionItemClicked(it!!) }
(activity as? MainActivity)?.showBottomNav(false)
val activity = activity
if (actionMode == null && activity is MainActivity) {
actionMode = activity.startActionModeAndToolbar(this)
activity.showBottomNav(false)
}
}
@@ -455,6 +444,10 @@ class LibraryController(
return true
}
override fun onCreateActionToolbar(menuInflater: MenuInflater, menu: Menu) {
menuInflater.inflate(R.menu.library_selection, menu)
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
val count = selectedMangas.size
if (count == 0) {
@@ -462,17 +455,17 @@ class LibraryController(
destroyActionModeIfNeeded()
} else {
mode.title = count.toString()
binding.actionToolbar.findItem(R.id.action_download_unread)?.isVisible = selectedMangas.any { it.source != LocalSource.ID }
}
return false
return true
}
override fun onPrepareActionToolbar(toolbar: ActionModeWithToolbar, menu: Menu) {
if (selectedMangas.isEmpty()) return
toolbar.findToolbarItem(R.id.action_download_unread)?.isVisible =
selectedMangas.any { it.source != LocalSource.ID }
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return onActionItemClicked(item)
}
private fun onActionItemClicked(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_move_to_category -> showChangeMangaCategoriesDialog()
R.id.action_download_unread -> downloadUnreadChapters()
@@ -486,12 +479,11 @@ class LibraryController(
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {
override fun onDestroyActionMode(mode: ActionMode) {
// Clear all the manga selections and notify child views.
selectedMangas.clear()
selectionRelay.call(LibrarySelectionEvent.Cleared())
binding.actionToolbar.hide()
(activity as? MainActivity)?.showBottomNav(true)
actionMode = null

View File

@@ -12,13 +12,13 @@ import android.view.Window
import android.widget.Toast
import androidx.appcompat.view.ActionMode
import androidx.core.animation.doOnEnd
import androidx.core.graphics.ColorUtils
import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import androidx.lifecycle.lifecycleScope
@@ -27,7 +27,6 @@ import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.Router
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
import dev.chrisbanes.insetter.applyInsetter
@@ -63,10 +62,12 @@ import eu.kanade.tachiyomi.ui.setting.SettingsMainController
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getThemeColor
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
import eu.kanade.tachiyomi.widget.ActionModeWithToolbar
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn
@@ -482,7 +483,11 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
tag = isTransparentWhenNotLifted
isTransparentWhenNotLifted = false
}
setToolbarScrolls(false)
// Color taken from m3_appbar_background
window.statusBarColor = ColorUtils.compositeColors(
getColor(R.color.m3_appbar_overlay_color),
getThemeColor(R.attr.colorSurface)
)
super.onSupportActionModeStarted(mode)
}
@@ -491,10 +496,15 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
isTransparentWhenNotLifted = (tag as? Boolean) ?: false
tag = null
}
setToolbarScrolls(true)
window.statusBarColor = getThemeColor(android.R.attr.statusBarColor)
super.onSupportActionModeFinished(mode)
}
fun startActionModeAndToolbar(modeCallback: ActionModeWithToolbar.Callback): ActionModeWithToolbar {
binding.actionToolbar.start(modeCallback)
return binding.actionToolbar
}
private suspend fun resetExitConfirmation() {
isConfirmingExit = true
val toast = toast(R.string.confirm_exit, Toast.LENGTH_LONG)
@@ -606,18 +616,6 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
binding.sideNav?.isVisible = visible
}
/**
* Sets toolbar CoordinatorLayout scroll flags
*/
private fun setToolbarScrolls(enabled: Boolean) = binding.toolbar.updateLayoutParams<AppBarLayout.LayoutParams> {
if (isTablet()) return@updateLayoutParams
scrollFlags = if (enabled) {
AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
} else {
0
}
}
private val nav: NavigationBarView
get() = binding.bottomNav ?: binding.sideNav!!

View File

@@ -14,7 +14,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.FloatRange
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.os.bundleOf
import androidx.core.view.ViewCompat
@@ -91,6 +90,7 @@ import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.shrinkOnScroll
import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.widget.ActionModeWithToolbar
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -106,7 +106,7 @@ import kotlin.math.min
class MangaController :
NucleusController<MangaControllerBinding, MangaPresenter>,
FabController,
ActionMode.Callback,
ActionModeWithToolbar.Callback,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
BaseChaptersAdapter.OnChapterClickListener,
@@ -161,7 +161,7 @@ class MangaController :
/**
* Action mode for multiple selection.
*/
private var actionMode: ActionMode? = null
private var actionMode: ActionModeWithToolbar? = null
/**
* Selected items. Used to restore selections after a rotation.
@@ -238,11 +238,6 @@ class MangaController :
it.layoutManager = LinearLayoutManager(view.context)
it.setHasFixedSize(true)
}
binding.actionToolbar.applyInsetter {
type(navigationBars = true) {
margin(bottom = true, horizontal = true)
}
}
if (manga == null || source == null) return
@@ -382,16 +377,19 @@ class MangaController :
val context = view?.context ?: return
val adapter = chaptersAdapter ?: return
val fab = actionFab ?: return
fab.isVisible = adapter.items.any { !it.read }
if (adapter.items.any { it.read }) {
fab.text = context.getString(R.string.action_resume)
}
if (adapter.items.any { !it.read }) {
fab.show()
} else {
fab.hide()
}
}
override fun onDestroyView(view: View) {
recyclerViewUpdatesToolbarTitleAlpha(false)
destroyActionModeIfNeeded()
binding.actionToolbar.destroy()
mangaInfoAdapter = null
chaptersHeaderAdapter = null
chaptersAdapter = null
@@ -978,11 +976,7 @@ class MangaController :
private fun createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this)
binding.actionToolbar.show(
actionMode!!,
R.menu.chapter_selection
) { onActionItemClicked(it!!) }
actionMode = (activity as MainActivity).startActionModeAndToolbar(this)
}
}
@@ -998,6 +992,10 @@ class MangaController :
return true
}
override fun onCreateActionToolbar(menuInflater: MenuInflater, menu: Menu) {
menuInflater.inflate(R.menu.chapter_selection, menu)
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
val count = chaptersAdapter?.selectedItemCount ?: 0
if (count == 0) {
@@ -1006,25 +1004,24 @@ class MangaController :
} else {
mode.title = count.toString()
val chapters = getSelectedChapters()
binding.actionToolbar.findItem(R.id.action_download)?.isVisible = !isLocalSource && chapters.any { !it.isDownloaded }
binding.actionToolbar.findItem(R.id.action_delete)?.isVisible = !isLocalSource && chapters.any { it.isDownloaded }
binding.actionToolbar.findItem(R.id.action_bookmark)?.isVisible = chapters.any { !it.chapter.bookmark }
binding.actionToolbar.findItem(R.id.action_remove_bookmark)?.isVisible = chapters.all { it.chapter.bookmark }
binding.actionToolbar.findItem(R.id.action_mark_as_read)?.isVisible = chapters.any { !it.chapter.read }
binding.actionToolbar.findItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read }
// Hide FAB to avoid interfering with the bottom action toolbar
actionFab?.isVisible = false
actionFab?.hide()
}
return false
return true
}
override fun onPrepareActionToolbar(toolbar: ActionModeWithToolbar, menu: Menu) {
val chapters = getSelectedChapters()
if (chapters.isEmpty()) return
toolbar.findToolbarItem(R.id.action_download)?.isVisible = !isLocalSource && chapters.any { !it.isDownloaded }
toolbar.findToolbarItem(R.id.action_delete)?.isVisible = !isLocalSource && chapters.any { it.isDownloaded }
toolbar.findToolbarItem(R.id.action_bookmark)?.isVisible = chapters.any { !it.chapter.bookmark }
toolbar.findToolbarItem(R.id.action_remove_bookmark)?.isVisible = chapters.all { it.chapter.bookmark }
toolbar.findToolbarItem(R.id.action_mark_as_read)?.isVisible = chapters.any { !it.chapter.read }
toolbar.findToolbarItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read }
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return onActionItemClicked(item)
}
private fun onActionItemClicked(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_select_all -> selectAll()
R.id.action_select_inverse -> selectInverse()
@@ -1041,11 +1038,13 @@ class MangaController :
}
override fun onDestroyActionMode(mode: ActionMode) {
binding.actionToolbar.hide()
chaptersAdapter?.mode = SelectableAdapter.Mode.SINGLE
chaptersAdapter?.clearSelection()
selectedChapters.clear()
actionMode = null
}
override fun onDestroyActionToolbar() {
updateFabVisibility()
}

View File

@@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.recyclerview.widget.LinearLayoutManager
import dev.chrisbanes.insetter.applyInsetter
@@ -29,6 +28,7 @@ import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.onAnimationsFinished
import eu.kanade.tachiyomi.widget.ActionModeWithToolbar
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import logcat.LogPriority
@@ -41,7 +41,7 @@ import reactivecircus.flowbinding.swiperefreshlayout.refreshes
class UpdatesController :
NucleusController<UpdatesControllerBinding, UpdatesPresenter>(),
RootController,
ActionMode.Callback,
ActionModeWithToolbar.Callback,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
FlexibleAdapter.OnUpdateListener,
@@ -52,7 +52,7 @@ class UpdatesController :
/**
* Action mode for multiple selection.
*/
private var actionMode: ActionMode? = null
private var actionMode: ActionModeWithToolbar? = null
/**
* Adapter containing the recent chapters.
@@ -81,11 +81,6 @@ class UpdatesController :
padding()
}
}
binding.actionToolbar.applyInsetter {
type(navigationBars = true) {
margin(bottom = true, horizontal = true)
}
}
view.context.notificationManager.cancel(Notifications.ID_NEW_CHAPTERS)
@@ -118,7 +113,6 @@ class UpdatesController :
override fun onDestroyView(view: View) {
destroyActionModeIfNeeded()
binding.actionToolbar.destroy()
adapter = null
super.onDestroyView(view)
}
@@ -175,15 +169,11 @@ class UpdatesController :
* @param position position of clicked item
*/
override fun onItemLongClick(position: Int) {
if (actionMode == null) {
actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
binding.actionToolbar.show(
actionMode!!,
R.menu.updates_chapter_selection
) { onActionItemClicked(it!!) }
(activity as? MainActivity)?.showBottomNav(false)
val activity = activity
if (actionMode == null && activity is MainActivity) {
actionMode = activity.startActionModeAndToolbar(this)
activity.showBottomNav(false)
}
toggleSelection(position)
}
@@ -341,6 +331,10 @@ class UpdatesController :
return true
}
override fun onCreateActionToolbar(menuInflater: MenuInflater, menu: Menu) {
menuInflater.inflate(R.menu.updates_chapter_selection, menu)
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
val count = adapter?.selectedItemCount ?: 0
if (count == 0) {
@@ -348,17 +342,19 @@ class UpdatesController :
destroyActionModeIfNeeded()
} else {
mode.title = count.toString()
val chapters = getSelectedChapters()
binding.actionToolbar.findItem(R.id.action_download)?.isVisible = chapters.any { !it.isDownloaded }
binding.actionToolbar.findItem(R.id.action_delete)?.isVisible = chapters.any { it.isDownloaded }
binding.actionToolbar.findItem(R.id.action_bookmark)?.isVisible = chapters.any { !it.bookmark }
binding.actionToolbar.findItem(R.id.action_remove_bookmark)?.isVisible = chapters.all { it.bookmark }
binding.actionToolbar.findItem(R.id.action_mark_as_read)?.isVisible = chapters.any { !it.chapter.read }
binding.actionToolbar.findItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read }
}
return true
}
return false
override fun onPrepareActionToolbar(toolbar: ActionModeWithToolbar, menu: Menu) {
val chapters = getSelectedChapters()
if (chapters.isEmpty()) return
toolbar.findToolbarItem(R.id.action_download)?.isVisible = chapters.any { !it.isDownloaded }
toolbar.findToolbarItem(R.id.action_delete)?.isVisible = chapters.any { it.isDownloaded }
toolbar.findToolbarItem(R.id.action_bookmark)?.isVisible = chapters.any { !it.bookmark }
toolbar.findToolbarItem(R.id.action_remove_bookmark)?.isVisible = chapters.all { it.bookmark }
toolbar.findToolbarItem(R.id.action_mark_as_read)?.isVisible = chapters.any { !it.chapter.read }
toolbar.findToolbarItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read }
}
/**
@@ -391,11 +387,10 @@ class UpdatesController :
* Called when ActionMode destroyed
* @param mode the ActionMode object
*/
override fun onDestroyActionMode(mode: ActionMode?) {
override fun onDestroyActionMode(mode: ActionMode) {
adapter?.mode = SelectableAdapter.Mode.IDLE
adapter?.clearSelection()
binding.actionToolbar.hide()
(activity as? MainActivity)?.showBottomNav(true)
actionMode = null