From 2ed01af72381c3abc8692af22bc528c93f24d530 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sun, 19 Dec 2021 02:16:26 +0700 Subject: [PATCH] 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 --- .../tachiyomi/ui/library/LibraryController.kt | 46 ++--- .../kanade/tachiyomi/ui/main/MainActivity.kt | 30 ++-- .../tachiyomi/ui/manga/MangaController.kt | 59 +++--- .../ui/recent/updates/UpdatesController.kt | 51 +++--- .../tachiyomi/widget/ActionModeWithToolbar.kt | 168 ++++++++++++++++++ .../kanade/tachiyomi/widget/ActionToolbar.kt | 73 -------- .../res/layout-sw720dp/action_toolbar.xml | 40 +++++ .../main/res/layout-sw720dp/main_activity.xml | 6 + .../res/layout-sw720dp/manga_controller.xml | 10 -- app/src/main/res/layout/action_toolbar.xml | 34 ++-- .../main/res/layout/library_controller.xml | 7 - app/src/main/res/layout/main_activity.xml | 6 + app/src/main/res/layout/manga_controller.xml | 7 - .../main/res/layout/updates_controller.xml | 7 - 14 files changed, 318 insertions(+), 226 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/ActionModeWithToolbar.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/ActionToolbar.kt create mode 100644 app/src/main/res/layout-sw720dp/action_toolbar.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 7e1ab59fef..846e432077 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -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(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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index d805543689..c892f10416 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -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() { 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() { 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() { binding.sideNav?.isVisible = visible } - /** - * Sets toolbar CoordinatorLayout scroll flags - */ - private fun setToolbarScrolls(enabled: Boolean) = binding.toolbar.updateLayoutParams { - 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!! diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 60fb4a9e39..d67d14e1d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -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, 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() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesController.kt index e0e81f8f4c..4b95af529a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesController.kt @@ -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(), 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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ActionModeWithToolbar.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ActionModeWithToolbar.kt new file mode 100644 index 0000000000..7c98423197 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/ActionModeWithToolbar.kt @@ -0,0 +1,168 @@ +package eu.kanade.tachiyomi.widget + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.animation.Animation +import android.view.animation.AnimationUtils +import android.widget.FrameLayout +import androidx.annotation.IdRes +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode +import androidx.core.view.isVisible +import dev.chrisbanes.insetter.applyInsetter +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.databinding.ActionToolbarBinding +import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale +import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener + +/** + * A toolbar holding only menu items. This view is supposed to be paired with [AppCompatActivity]'s [ActionMode]. + * + * @see Callback + */ +class ActionModeWithToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + FrameLayout(context, attrs) { + + init { + clipToPadding = false + applyInsetter { + type(navigationBars = true) { + padding(bottom = true, horizontal = true) + } + } + } + + private val binding = ActionToolbarBinding.inflate(LayoutInflater.from(context), this, true) + + private var callback: Callback? = null + + private var actionMode: ActionMode? = null + private val actionModeCallback = object : ActionMode.Callback { + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + callback?.onCreateActionToolbar(mode.menuInflater, binding.menu.menu) + binding.menu.setOnMenuItemClickListener { onActionItemClicked(mode, it) } + binding.root.isVisible = true + val bottomAnimation = AnimationUtils.loadAnimation(context, R.anim.bottom_sheet_slide_in) + bottomAnimation.applySystemAnimatorScale(context) + binding.root.startAnimation(bottomAnimation) + + return callback?.onCreateActionMode(mode, menu) ?: false + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + callback?.onPrepareActionToolbar(this@ActionModeWithToolbar, binding.menu.menu) + return callback?.onPrepareActionMode(mode, menu) ?: false + } + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return callback?.onActionItemClicked(mode, item) ?: false + } + + override fun onDestroyActionMode(mode: ActionMode) { + callback?.onDestroyActionMode(mode) + + val bottomAnimation = AnimationUtils.loadAnimation(context, R.anim.bottom_sheet_slide_out).apply { + applySystemAnimatorScale(context) + setAnimationListener( + object : SimpleAnimationListener() { + override fun onAnimationEnd(animation: Animation) { + binding.root.isVisible = false + binding.menu.menu.clear() + binding.menu.setOnMenuItemClickListener(null) + + callback?.onDestroyActionToolbar() + callback = null + actionMode = null + } + } + ) + } + binding.root.startAnimation(bottomAnimation) + } + } + + fun start(callback: Callback) { + val context = context + if (context !is AppCompatActivity) { + throw IllegalStateException("AppCompatActivity is needed to start this view") + } + if (actionMode == null) { + this.callback = callback + actionMode = context.startSupportActionMode(actionModeCallback) + } + } + + fun finish() { + actionMode?.finish() + } + + /** + * Gets a menu item if found. + */ + fun findToolbarItem(@IdRes itemId: Int): MenuItem? { + return binding.menu.menu.findItem(itemId) + } + + override fun invalidate() { + super.invalidate() + actionMode?.invalidate() + } + + interface Callback { + /** + * Called when action mode is first created. The menu supplied will be used to + * generate action buttons for the action mode. + * + * @param mode ActionMode being created + * @param menu Menu used to populate action buttons + * @return true if the action mode should be created, false if entering this + * mode should be aborted. + */ + fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean + + /** + * [onCreateActionMode] but for the bottom toolbar + */ + fun onCreateActionToolbar(menuInflater: MenuInflater, menu: Menu) + + /** + * Called to refresh an action mode's action menu whenever it is invalidated. + * + * @param mode ActionMode being prepared + * @param menu Menu used to populate action buttons + * @return true if the menu or action mode was updated, false otherwise. + */ + fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean + + /** + * [onPrepareActionMode] but for the bottom toolbar + */ + fun onPrepareActionToolbar(toolbar: ActionModeWithToolbar, menu: Menu) + + /** + * Called to report a user click on an action button. + * + * @param mode The current ActionMode + * @param item The item that was clicked + * @return true if this callback handled the event, false if the standard MenuItem + * invocation should continue. + */ + fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean + + /** + * Called when an action mode is about to be exited and destroyed. + * + * @param mode The current ActionMode being destroyed + */ + fun onDestroyActionMode(mode: ActionMode) + + /** + * Called when the action toolbar is finished exiting + */ + fun onDestroyActionToolbar() {} + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ActionToolbar.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ActionToolbar.kt deleted file mode 100644 index 3bcab8a5f5..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ActionToolbar.kt +++ /dev/null @@ -1,73 +0,0 @@ -package eu.kanade.tachiyomi.widget - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.animation.Animation -import android.view.animation.AnimationUtils -import android.widget.FrameLayout -import androidx.annotation.IdRes -import androidx.annotation.MenuRes -import androidx.appcompat.view.ActionMode -import androidx.core.view.isVisible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.ActionToolbarBinding -import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale -import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener - -/** - * A toolbar holding only menu items. - */ -class ActionToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - FrameLayout(context, attrs) { - - private val binding = ActionToolbarBinding.inflate(LayoutInflater.from(context), this, true) - - /** - * Remove menu items and remove listener. - */ - fun destroy() { - binding.menu.menu.clear() - binding.menu.setOnMenuItemClickListener(null) - } - - /** - * Gets a menu item if found. - */ - fun findItem(@IdRes itemId: Int): MenuItem? { - return binding.menu.menu.findItem(itemId) - } - - /** - * Show the menu toolbar using the provided ActionMode's context to inflate the items. - */ - fun show(mode: ActionMode, @MenuRes menuRes: Int, listener: (item: MenuItem?) -> Boolean) { - // Avoid re-inflating the menu - if (binding.menu.menu.size() == 0) { - mode.menuInflater.inflate(menuRes, binding.menu.menu) - binding.menu.setOnMenuItemClickListener { listener(it) } - } - - binding.actionToolbar.isVisible = true - val bottomAnimation = AnimationUtils.loadAnimation(context, R.anim.enter_from_bottom) - bottomAnimation.applySystemAnimatorScale(context) - binding.actionToolbar.startAnimation(bottomAnimation) - } - - /** - * Hide the menu toolbar. - */ - fun hide() { - val bottomAnimation = AnimationUtils.loadAnimation(context, R.anim.exit_to_bottom) - bottomAnimation.applySystemAnimatorScale(context) - bottomAnimation.setAnimationListener( - object : SimpleAnimationListener() { - override fun onAnimationEnd(animation: Animation) { - binding.actionToolbar.isVisible = false - } - } - ) - binding.actionToolbar.startAnimation(bottomAnimation) - } -} diff --git a/app/src/main/res/layout-sw720dp/action_toolbar.xml b/app/src/main/res/layout-sw720dp/action_toolbar.xml new file mode 100644 index 0000000000..31f695c9e6 --- /dev/null +++ b/app/src/main/res/layout-sw720dp/action_toolbar.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-sw720dp/main_activity.xml b/app/src/main/res/layout-sw720dp/main_activity.xml index b10673facf..daa4b0a8b2 100644 --- a/app/src/main/res/layout-sw720dp/main_activity.xml +++ b/app/src/main/res/layout-sw720dp/main_activity.xml @@ -99,6 +99,12 @@ + + diff --git a/app/src/main/res/layout-sw720dp/manga_controller.xml b/app/src/main/res/layout-sw720dp/manga_controller.xml index d90991b0a4..52f58ce4ce 100644 --- a/app/src/main/res/layout-sw720dp/manga_controller.xml +++ b/app/src/main/res/layout-sw720dp/manga_controller.xml @@ -43,16 +43,6 @@ app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/chapters_item" /> - - diff --git a/app/src/main/res/layout/action_toolbar.xml b/app/src/main/res/layout/action_toolbar.xml index ae507c0ee7..301154bf17 100644 --- a/app/src/main/res/layout/action_toolbar.xml +++ b/app/src/main/res/layout/action_toolbar.xml @@ -1,35 +1,27 @@ - - + android:layout_height="?attr/actionBarSize" + android:layout_gravity="bottom" + app:contentInsetEnd="8dp" + app:contentInsetStart="8dp"> - + android:layout_height="match_parent" + android:layout_gravity="center" /> - + - - - - - + diff --git a/app/src/main/res/layout/library_controller.xml b/app/src/main/res/layout/library_controller.xml index 9fdc7de35c..7b60d7af75 100644 --- a/app/src/main/res/layout/library_controller.xml +++ b/app/src/main/res/layout/library_controller.xml @@ -27,13 +27,6 @@ - - + + - - diff --git a/app/src/main/res/layout/updates_controller.xml b/app/src/main/res/layout/updates_controller.xml index 050d5a1aac..8982a067cc 100644 --- a/app/src/main/res/layout/updates_controller.xml +++ b/app/src/main/res/layout/updates_controller.xml @@ -27,13 +27,6 @@ app:fastScrollerBubbleEnabled="false" tools:visibility="visible" /> - -