mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	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:
		| @@ -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 | ||||
|   | ||||
| @@ -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!! | ||||
|  | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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() {} | ||||
|     } | ||||
| } | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								app/src/main/res/layout-sw720dp/action_toolbar.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/src/main/res/layout-sw720dp/action_toolbar.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipToPadding="false" | ||||
|     android:visibility="gone" | ||||
|     tools:visibility="visible"> | ||||
|  | ||||
|     <com.google.android.material.card.MaterialCardView | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_margin="12dp" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintHorizontal_bias="1.0" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" | ||||
|         app:layout_constraintWidth_default="percent" | ||||
|         app:layout_constraintWidth_percent=".5"> | ||||
|  | ||||
|         <com.google.android.material.appbar.MaterialToolbar | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="?attr/actionBarSize" | ||||
|             android:layout_gravity="bottom" | ||||
|             app:contentInsetEnd="8dp" | ||||
|             app:contentInsetStart="8dp"> | ||||
|  | ||||
|             <androidx.appcompat.widget.ActionMenuView | ||||
|                 android:id="@+id/menu" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:layout_gravity="center" /> | ||||
|  | ||||
|         </com.google.android.material.appbar.MaterialToolbar> | ||||
|  | ||||
|     </com.google.android.material.card.MaterialCardView> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
| @@ -99,6 +99,12 @@ | ||||
|  | ||||
|     </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.ActionModeWithToolbar | ||||
|         android:id="@+id/action_toolbar" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="bottom" /> | ||||
|  | ||||
|     <include | ||||
|         android:id="@+id/fab_layout" | ||||
|         layout="@layout/main_activity_fab" /> | ||||
|   | ||||
| @@ -43,16 +43,6 @@ | ||||
|                 app:layout_constraintTop_toTopOf="parent" | ||||
|                 tools:listitem="@layout/chapters_item" /> | ||||
|  | ||||
|             <eu.kanade.tachiyomi.widget.ActionToolbar | ||||
|                 android:id="@+id/action_toolbar" | ||||
|                 android:layout_width="0dp" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_gravity="bottom" | ||||
|                 app:layout_constraintBottom_toBottomOf="parent" | ||||
|                 app:layout_constraintEnd_toEndOf="@+id/chapters_recycler" | ||||
|                 app:layout_constraintStart_toStartOf="@+id/chapters_recycler" | ||||
|                 app:layout_dodgeInsetEdges="bottom" /> | ||||
|  | ||||
|         </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|     </eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout> | ||||
|   | ||||
| @@ -1,35 +1,27 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:id="@+id/action_toolbar" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:layout_margin="12dp" | ||||
|     android:clipToPadding="false" | ||||
|     android:padding="8dp" | ||||
|     android:visibility="gone" | ||||
|     tools:visibility="visible"> | ||||
|  | ||||
|     <com.google.android.material.card.MaterialCardView | ||||
|     <com.google.android.material.appbar.MaterialToolbar | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:cardCornerRadius="@dimen/dialog_radius"> | ||||
|         android:layout_height="?attr/actionBarSize" | ||||
|         android:layout_gravity="bottom" | ||||
|         app:contentInsetEnd="8dp" | ||||
|         app:contentInsetStart="8dp"> | ||||
|  | ||||
|         <com.google.android.material.appbar.MaterialToolbar | ||||
|         <androidx.appcompat.widget.ActionMenuView | ||||
|             android:id="@+id/menu" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="?attr/actionBarSize" | ||||
|             android:layout_gravity="bottom" | ||||
|             app:contentInsetEnd="8dp" | ||||
|             app:contentInsetStart="8dp"> | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_gravity="center" /> | ||||
|  | ||||
|             <androidx.appcompat.widget.ActionMenuView | ||||
|                 android:id="@+id/menu" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:layout_gravity="center" /> | ||||
|     </com.google.android.material.appbar.MaterialToolbar> | ||||
|  | ||||
|         </com.google.android.material.appbar.MaterialToolbar> | ||||
|  | ||||
|     </com.google.android.material.card.MaterialCardView> | ||||
|  | ||||
| </FrameLayout> | ||||
| </com.google.android.material.card.MaterialCardView> | ||||
|   | ||||
| @@ -27,13 +27,6 @@ | ||||
|  | ||||
|     </LinearLayout> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.ActionToolbar | ||||
|         android:id="@+id/action_toolbar" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="bottom" | ||||
|         app:layout_dodgeInsetEdges="bottom" /> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.EmptyView | ||||
|         android:id="@+id/empty_view" | ||||
|         android:layout_width="wrap_content" | ||||
|   | ||||
| @@ -74,6 +74,12 @@ | ||||
|         android:id="@+id/fab_layout" | ||||
|         layout="@layout/main_activity_fab" /> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.ActionModeWithToolbar | ||||
|         android:id="@+id/action_toolbar" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="bottom" /> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView | ||||
|         android:id="@+id/bottom_nav" | ||||
|         android:layout_width="match_parent" | ||||
|   | ||||
| @@ -33,11 +33,4 @@ | ||||
|         app:fastScrollerBubbleEnabled="false" | ||||
|         tools:visibility="visible" /> | ||||
|  | ||||
|     <eu.kanade.tachiyomi.widget.ActionToolbar | ||||
|         android:id="@+id/action_toolbar" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="bottom" | ||||
|         app:layout_dodgeInsetEdges="bottom" /> | ||||
|  | ||||
| </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
|   | ||||
| @@ -27,13 +27,6 @@ | ||||
|             app:fastScrollerBubbleEnabled="false" | ||||
|             tools:visibility="visible" /> | ||||
|  | ||||
|         <eu.kanade.tachiyomi.widget.ActionToolbar | ||||
|             android:id="@+id/action_toolbar" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_gravity="bottom" | ||||
|             app:layout_dodgeInsetEdges="bottom" /> | ||||
|  | ||||
|         <eu.kanade.tachiyomi.widget.EmptyView | ||||
|             android:id="@+id/empty_view" | ||||
|             android:layout_width="wrap_content" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user