From 59e11ff486e9ba857987f2924d08c56df0993cba Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Mon, 16 Oct 2023 20:26:51 -0700 Subject: [PATCH] Add back pressed animation + update controller push/pop animation Likely am gonna revert for the stable just to save myself the headache Limiting the new animations to Android 10+, new animations will still play on 10+ devices, but wont keep the view on low ram devices --- app/build.gradle.kts | 4 +- .../ui/base/controller/BaseController.kt | 16 ++--- .../base/controller/CrossFadeChangeHandler.kt | 61 ++++++++++++++++++ .../controller/OneWayFadeChangeHandler.kt | 18 +++--- .../kanade/tachiyomi/ui/main/MainActivity.kt | 42 +++++++++--- .../ui/manga/MangaDetailsController.kt | 10 +-- .../tachiyomi/ui/recents/RecentsController.kt | 51 ++++++++------- .../ui/setting/SettingsController.kt | 12 +++- .../tachiyomi/ui/source/BrowseController.kt | 12 ++-- .../globalsearch/GlobalSearchController.kt | 3 +- .../util/view/ControllerExtensions.kt | 64 +++++++++++++++++-- .../res/layout/browse_source_controller.xml | 1 + 12 files changed, 224 insertions(+), 70 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/CrossFadeChangeHandler.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0cb8f8f2d1..5995f1197c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -269,9 +269,9 @@ dependencies { implementation("com.getkeepsafe.taptargetview:taptargetview:1.13.3") // Conductor - val conductorVersion = "3.0.0" + val conductorVersion = "4.0.0-preview-3" implementation("com.bluelinelabs:conductor:$conductorVersion") - implementation("com.github.tachiyomiorg:conductor-support-preference:$conductorVersion") + implementation("com.github.tachiyomiorg:conductor-support-preference:3.0.0") // Shizuku val shizukuVersion = "12.1.0" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt index 9c590c0740..8746abe950 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt @@ -8,7 +8,6 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.activity.BackEventCompat import androidx.appcompat.app.AppCompatActivity import androidx.core.view.forEach import androidx.core.view.isVisible @@ -18,6 +17,7 @@ import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import eu.kanade.tachiyomi.ui.main.MainActivity +import eu.kanade.tachiyomi.util.view.BackHandlerControllerInterface import eu.kanade.tachiyomi.util.view.activityBinding import eu.kanade.tachiyomi.util.view.isControllerVisible import eu.kanade.tachiyomi.util.view.removeQueryListener @@ -27,7 +27,7 @@ import kotlinx.coroutines.cancel import timber.log.Timber abstract class BaseController(bundle: Bundle? = null) : - Controller(bundle) { + Controller(bundle), BackHandlerControllerInterface { lateinit var binding: VB lateinit var viewScope: CoroutineScope @@ -72,12 +72,14 @@ abstract class BaseController(bundle: Bundle? = null) : open fun onViewCreated(view: View) { } override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { - if (type.isEnter) { + if (type.isEnter && isControllerVisible) { setTitle() + } else if (type.isEnter) { + view?.alpha = 0f } else { removeQueryListener() } - setHasOptionsMenu(type.isEnter) + setHasOptionsMenu(type.isEnter && isControllerVisible) super.onChangeStarted(handler, type) } @@ -95,12 +97,6 @@ abstract class BaseController(bundle: Bundle? = null) : open fun canStillGoBack(): Boolean { return false } - open fun handleOnBackStarted(backEvent: BackEventCompat) {} - - open fun handleOnBackProgressed(backEvent: BackEventCompat) {} - - open fun handleOnBackCancelled() {} - open val mainRecycler: RecyclerView? get() = null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/CrossFadeChangeHandler.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/CrossFadeChangeHandler.kt new file mode 100644 index 0000000000..dfc02e7ea1 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/CrossFadeChangeHandler.kt @@ -0,0 +1,61 @@ +package eu.kanade.tachiyomi.ui.base.controller + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.view.View +import android.view.ViewGroup +import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler + +class CrossFadeChangeHandler : AnimatorChangeHandler { + constructor() : super() + constructor(removesFromViewOnPush: Boolean) : super(removesFromViewOnPush) + constructor(duration: Long) : super(duration) + constructor(duration: Long, removesFromViewOnPush: Boolean) : super( + duration, + removesFromViewOnPush, + ) + + override fun getAnimator( + container: ViewGroup, + from: View?, + to: View?, + isPush: Boolean, + toAddedToContainer: Boolean, + ): Animator { + val animatorSet = AnimatorSet() + if (to != null) { + val start = if (toAddedToContainer) 0F else to.alpha + animatorSet.play(ObjectAnimator.ofFloat(to, View.ALPHA, start, 1f)) + } + if (from != null) { + animatorSet.play(ObjectAnimator.ofFloat(from, View.ALPHA, 0f)) + } + if (isPush) { + if (from != null) { + animatorSet.play(ObjectAnimator.ofFloat(from, View.TRANSLATION_X, -from.width.toFloat() * 0.2f)) + } + if (to != null) { + animatorSet.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, to.width.toFloat() * 0.2f, 0f)) + } + } else { + if (from != null) { + animatorSet.play(ObjectAnimator.ofFloat(from, View.TRANSLATION_X, from.width.toFloat() * 0.2f)) + } + if (to != null) { + // Allow this to have a nice transition when coming off an aborted push animation + val fromLeft = from?.translationX ?: 0F + animatorSet.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, fromLeft - to.width * 0.2f, 0f)) + } + } + return animatorSet + } + + override fun resetFromView(from: View) { + from.translationX = 0f + } + + override fun copy(): ControllerChangeHandler = + CrossFadeChangeHandler(animationDuration, removesFromViewOnPush) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/OneWayFadeChangeHandler.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/OneWayFadeChangeHandler.kt index 71bef32e8e..e0bde29c1c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/OneWayFadeChangeHandler.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/OneWayFadeChangeHandler.kt @@ -6,12 +6,13 @@ import android.animation.ObjectAnimator import android.view.View import android.view.ViewGroup import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler import com.bluelinelabs.conductor.changehandler.FadeChangeHandler /** * A variation of [FadeChangeHandler] that only fades in. */ -class OneWayFadeChangeHandler : FadeChangeHandler { +class OneWayFadeChangeHandler : AnimatorChangeHandler { constructor() constructor(removesFromViewOnPush: Boolean) : super(removesFromViewOnPush) constructor(duration: Long) : super(duration) @@ -20,7 +21,6 @@ class OneWayFadeChangeHandler : FadeChangeHandler { removesFromViewOnPush, ) - var fadeOut = true override fun getAnimator( container: ViewGroup, from: View?, @@ -34,17 +34,17 @@ class OneWayFadeChangeHandler : FadeChangeHandler { animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, start, 1f)) } - if (from != null && (!isPush || removesFromViewOnPush())) { - if (fadeOut) { - animator.play(ObjectAnimator.ofFloat(from, View.ALPHA, 0f)) - } else { - container.removeView(from) - } + if (from != null && (!isPush || removesFromViewOnPush)) { + container.removeView(from) } return animator } + override fun resetFromView(from: View) { + from.alpha = 1f + } + override fun copy(): ControllerChangeHandler { - return OneWayFadeChangeHandler(animationDuration, removesFromViewOnPush()) + return OneWayFadeChangeHandler(animationDuration, removesFromViewOnPush) } } 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 e75c95b0b2..f914d1edb9 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 @@ -27,7 +27,6 @@ import android.view.Window import android.view.WindowManager import androidx.activity.BackEventCompat import androidx.activity.OnBackPressedCallback -import androidx.activity.addCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.IdRes import androidx.appcompat.view.ActionMode @@ -115,6 +114,7 @@ import eu.kanade.tachiyomi.util.system.materialAlertDialog import eu.kanade.tachiyomi.util.system.prepareSideNavContext import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat import eu.kanade.tachiyomi.util.system.toast +import eu.kanade.tachiyomi.util.view.BackHandlerControllerInterface import eu.kanade.tachiyomi.util.view.backgroundColor import eu.kanade.tachiyomi.util.view.blurBehindWindow import eu.kanade.tachiyomi.util.view.canStillGoBack @@ -212,7 +212,7 @@ open class MainActivity : BaseActivity() { get() = max(binding.toolbar.height, binding.cardFrame.height, binding.appBar.attrToolbarHeight) private var actionMode: ActionMode? = null - var backPressedCallback: OnBackPressedCallback? = null + private var backPressedCallback: OnBackPressedCallback? = null private val backCallback = { pressingBack() reEnableBackPressedCallBack() @@ -253,23 +253,41 @@ open class MainActivity : BaseActivity() { super.onCreate(savedInstanceState) backPressedCallback = object : OnBackPressedCallback(enabled = true) { + var controllerHandlesBackPress = false override fun handleOnBackPressed() { backCallback() } override fun handleOnBackStarted(backEvent: BackEventCompat) { - val controller = router.backstack.lastOrNull()?.controller as? BaseController<*> - controller?.handleOnBackStarted(backEvent) + controllerHandlesBackPress = false + if (!( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + ViewCompat.getRootWindowInsets(window.decorView) + ?.isVisible(WindowInsetsCompat.Type.ime()) == true + ) && + actionMode == null && + !(binding.searchToolbar.hasExpandedActionView() && binding.cardFrame.isVisible) + ) { + controllerHandlesBackPress = true + } + if (controllerHandlesBackPress) { + val controller = router.backstack.lastOrNull()?.controller as? BackHandlerControllerInterface + controller?.handleOnBackStarted(backEvent) + } } override fun handleOnBackProgressed(backEvent: BackEventCompat) { - val controller = router.backstack.lastOrNull()?.controller as? BaseController<*> - controller?.handleOnBackProgressed(backEvent) + if (controllerHandlesBackPress) { + val controller = router.backstack.lastOrNull()?.controller as? BackHandlerControllerInterface + controller?.handleOnBackProgressed(backEvent) + } } override fun handleOnBackCancelled() { - val controller = router.backstack.lastOrNull()?.controller as? BaseController<*> - controller?.handleOnBackCancelled() + if (controllerHandlesBackPress) { + val controller = router.backstack.lastOrNull()?.controller as? BackHandlerControllerInterface + controller?.handleOnBackCancelled() + } } } onBackPressedDispatcher.addCallback(backPressedCallback!!) @@ -501,6 +519,7 @@ open class MainActivity : BaseActivity() { container: ViewGroup, handler: ControllerChangeHandler, ) { + to?.view?.alpha = 1f syncActivityViewWithController(to, from, isPush) binding.appBar.isVisible = true binding.appBar.alpha = 1f @@ -519,6 +538,9 @@ open class MainActivity : BaseActivity() { ) { nav.translationY = 0f showDLQueueTutorial() + if (!(from is DialogController || to is DialogController) && from != null) { + from.view?.alpha = 0f + } if (router.backstackSize == 1) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R && !isPush) { window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) @@ -705,8 +727,8 @@ open class MainActivity : BaseActivity() { if (insets == null) return window.navigationBarColor = when { Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1 -> { - // basically if in landscape on a phone - // For lollipop, draw opaque nav bar + // basically if in landscape on a phone, solid black bar + // otherwise translucent dark theme or black if light theme when { insets.hasSideNavBar() -> Color.BLACK isInNightMode() -> ColorUtils.setAlphaComponent( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index fc748cd109..58710ec595 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -661,10 +661,12 @@ class MangaDetailsController : super.onChangeStarted(handler, type) isPushing = true if (type.isEnter) { - activityBinding?.appBar?.y = 0f - activityBinding?.appBar?.updateAppBarAfterY(binding.recycler) - updateToolbarTitleAlpha(0f) - setStatusBarAndToolbar() + if (isControllerVisible) { + activityBinding?.appBar?.y = 0f + activityBinding?.appBar?.updateAppBarAfterY(binding.recycler) + updateToolbarTitleAlpha(0f) + setStatusBarAndToolbar() + } } else { if (router.backstack.lastOrNull()?.controller is DialogController) { return diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt index 0f54ba60de..d56265b23d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt @@ -495,12 +495,16 @@ class RecentsController(bundle: Bundle? = null) : override fun handleOnBackProgressed(backEvent: BackEventCompat) { if (showingDownloads) { binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.updateBackProgress(backEvent) + } else { + super.handleOnBackProgressed(backEvent) } } override fun handleOnBackCancelled() { if (showingDownloads) { binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.cancelBackProgress() + } else { + super.handleOnBackCancelled() } } @@ -889,30 +893,33 @@ class RecentsController(bundle: Bundle? = null) : if (type.isEnter) { if (type == ControllerChangeType.POP_ENTER) presenter.onCreate() binding.downloadBottomSheet.dlBottomSheet.dismiss() - activityBinding?.mainTabs?.let { tabs -> - tabs.removeAllTabs() - tabs.clearOnTabSelectedListeners() - val selectedTab = presenter.viewType - RecentsViewType.entries.forEach { viewType -> - tabs.addTab( - tabs.newTab().setText(viewType.stringRes).also { tab -> - tab.view.compatToolTipText = null + if (isControllerVisible) { + activityBinding?.mainTabs?.let { tabs -> + tabs.removeAllTabs() + tabs.clearOnTabSelectedListeners() + val selectedTab = presenter.viewType + RecentsViewType.entries.forEach { viewType -> + tabs.addTab( + tabs.newTab().setText(viewType.stringRes).also { tab -> + tab.view.compatToolTipText = null + }, + viewType == selectedTab, + ) + } + tabs.addOnTabSelectedListener( + object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + setViewType(RecentsViewType.valueOf(tab?.position)) + } + + override fun onTabUnselected(tab: TabLayout.Tab?) {} + override fun onTabReselected(tab: TabLayout.Tab?) { + binding.recycler.smoothScrollToTop() + } }, - viewType == selectedTab, ) + (activity as? MainActivity)?.showTabBar(true) } - tabs.addOnTabSelectedListener( - object : TabLayout.OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab?) { - setViewType(RecentsViewType.valueOf(tab?.position)) - } - override fun onTabUnselected(tab: TabLayout.Tab?) {} - override fun onTabReselected(tab: TabLayout.Tab?) { - binding.recycler.smoothScrollToTop() - } - }, - ) - (activity as? MainActivity)?.showTabBar(true) } } else { val lastController = router.backstack.lastOrNull()?.controller @@ -929,7 +936,7 @@ class RecentsController(bundle: Bundle? = null) : if (type == ControllerChangeType.POP_ENTER) { setBottomPadding() } - if (type.isEnter) { + if (type.isEnter && isControllerVisible) { updateTitleAndMenu() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt index 20daac5fb1..dacfe5e64c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt @@ -26,7 +26,10 @@ import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.system.getResourceColor +import eu.kanade.tachiyomi.util.view.BackHandlerControllerInterface import eu.kanade.tachiyomi.util.view.activityBinding +import eu.kanade.tachiyomi.util.view.backgroundColor +import eu.kanade.tachiyomi.util.view.isControllerVisible import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset import kotlinx.coroutines.MainScope @@ -34,7 +37,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Locale -abstract class SettingsController : PreferenceController() { +abstract class SettingsController : PreferenceController(), BackHandlerControllerInterface { var preferenceKey: String? = null val preferences: PreferencesHelper = Injekt.get() @@ -47,6 +50,7 @@ abstract class SettingsController : PreferenceController() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { val view = super.onCreateView(inflater, container, savedInstanceState) + view.backgroundColor = view.context.getResourceColor(R.attr.background) scrollViewWith(listView, padBottom = true) return view } @@ -123,10 +127,12 @@ abstract class SettingsController : PreferenceController() { } override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { - if (type.isEnter) { + if (type.isEnter && isControllerVisible) { setTitle() + } else if (type.isEnter) { + view?.alpha = 0f } - setHasOptionsMenu(type.isEnter) + setHasOptionsMenu(type.isEnter && isControllerVisible) super.onChangeStarted(handler, type) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt index 58e00fae33..fe4b5c4603 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt @@ -358,7 +358,7 @@ class BrowseController : val controller = ExtensionFilterController() router.pushController( RouterTransaction.with(controller) - .popChangeHandler(SettingsSourcesFadeChangeHandler()) + .popChangeHandler(FadeChangeHandler()) .pushChangeHandler(FadeChangeHandler()), ) } @@ -494,12 +494,16 @@ class BrowseController : override fun handleOnBackProgressed(backEvent: BackEventCompat) { if (showingExtensions && !binding.bottomSheet.root.canStillGoBack()) { binding.bottomSheet.root.sheetBehavior?.updateBackProgress(backEvent) + } else { + super.handleOnBackProgressed(backEvent) } } override fun handleOnBackCancelled() { if (showingExtensions && !binding.bottomSheet.root.canStillGoBack()) { binding.bottomSheet.root.sheetBehavior?.cancelBackProgress() + } else { + super.handleOnBackCancelled() } } @@ -531,7 +535,7 @@ class BrowseController : binding.bottomSheet.root.updateExtTitle() binding.bottomSheet.root.presenter.refreshExtensions() presenter.updateSources() - if (type.isEnter) { + if (type.isEnter && isControllerVisible) { activityBinding?.appBar?.doOnNextLayout { activityBinding?.appBar?.y = 0f activityBinding?.appBar?.updateAppBarAfterY(binding.sourceRecycler) @@ -696,7 +700,7 @@ class BrowseController : val controller = SettingsSourcesController() router.pushController( RouterTransaction.with(controller) - .popChangeHandler(SettingsSourcesFadeChangeHandler()) + .popChangeHandler(FadeChangeHandler()) .pushChangeHandler(FadeChangeHandler()), ) } @@ -733,8 +737,6 @@ class BrowseController : } } - class SettingsSourcesFadeChangeHandler : FadeChangeHandler() - @Parcelize data class SmartSearchConfig(val origTitle: String, val origMangaId: Long) : Parcelable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt index 3a29ec5170..5f536c3979 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt @@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController import eu.kanade.tachiyomi.util.addOrRemoveToFavorites import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat import eu.kanade.tachiyomi.util.view.activityBinding +import eu.kanade.tachiyomi.util.view.isControllerVisible import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.snack @@ -169,7 +170,7 @@ open class GlobalSearchController( override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) - if (type.isEnter) { + if (type.isEnter && isControllerVisible) { val searchView = activityBinding?.searchToolbar?.searchView ?: return val searchItem = activityBinding?.searchToolbar?.searchItem ?: return searchItem.expandActionView() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt index db92d00966..26ae0945a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.util.view import android.Manifest import android.animation.Animator import android.animation.ValueAnimator +import android.app.ActivityManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -18,10 +19,13 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.inputmethod.InputMethodManager import android.widget.FrameLayout import android.widget.ImageView +import androidx.activity.BackEventCompat +import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.appcompat.widget.SearchView import androidx.cardview.widget.CardView import androidx.core.content.ContextCompat +import androidx.core.content.getSystemService import androidx.core.graphics.ColorUtils import androidx.core.math.MathUtils import androidx.core.net.toUri @@ -45,6 +49,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.backup.BackupCreatorJob import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -52,6 +57,7 @@ import eu.kanade.tachiyomi.databinding.MainActivityBinding import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.DialogController +import eu.kanade.tachiyomi.ui.base.controller.CrossFadeChangeHandler import eu.kanade.tachiyomi.ui.base.controller.OneWayFadeChangeHandler import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface import eu.kanade.tachiyomi.ui.main.MainActivity @@ -70,6 +76,7 @@ import eu.kanade.tachiyomi.widget.StatefulNestedScrollView import uy.kohesive.injekt.injectLazy import kotlin.math.abs import kotlin.math.max +import kotlin.math.pow import kotlin.math.roundToInt import kotlin.random.Random @@ -664,7 +671,9 @@ fun Controller.moveRecyclerViewUp(allTheWayUp: Boolean = false, scrollUpAnyway: val recycler = mainRecyclerView ?: return val activityBinding = activityBinding ?: return val appBarOffset = activityBinding.appBar.toolbarDistanceToTop - if (allTheWayUp && recycler.computeVerticalScrollOffset() - recycler.paddingTop <= fullAppBarHeight ?: activityBinding.appBar.preLayoutHeight) { + if (allTheWayUp && recycler.computeVerticalScrollOffset() - recycler.paddingTop <= + (fullAppBarHeight ?: activityBinding.appBar.preLayoutHeight) + ) { (recycler.layoutManager as? LinearLayoutManager)?.scrollToPosition(0) (recycler.layoutManager as? StaggeredGridLayoutManager)?.scrollToPosition(0) recycler.post { @@ -782,14 +791,27 @@ fun Controller.requestFilePermissionsSafe( } fun Controller.withFadeTransaction(): RouterTransaction { + val isLowRam by lazy { activity?.getSystemService()?.isLowRamDevice == true } return RouterTransaction.with(this) - .pushChangeHandler(OneWayFadeChangeHandler()) - .popChangeHandler(OneWayFadeChangeHandler()) + .pushChangeHandler( + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + FadeChangeHandler() + } else { + CrossFadeChangeHandler(duration = 250, removesFromViewOnPush = isLowRam) + }, + ) + .popChangeHandler( + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + FadeChangeHandler() + } else { + CrossFadeChangeHandler(duration = 150, removesFromViewOnPush = isLowRam) + }, + ) } fun Controller.withFadeInTransaction(): RouterTransaction { return RouterTransaction.with(this) - .pushChangeHandler(OneWayFadeChangeHandler().apply { fadeOut = false }) + .pushChangeHandler(FadeChangeHandler()) .popChangeHandler(OneWayFadeChangeHandler()) } @@ -830,3 +852,37 @@ fun Router.canStillGoBack(): Boolean { } return false } + +interface BackHandlerControllerInterface { + fun handleOnBackStarted(backEvent: BackEventCompat) { + } + + @CallSuper + fun handleOnBackProgressed(backEvent: BackEventCompat) { + if (this !is Controller) return + if (router.backstackSize > 1) { + val progress = ((backEvent.progress.takeIf { it > 0.001f } ?: 0f) * 0.5f).pow(0.6f) + view?.let { view -> + view.alpha = 1f - progress + view.x = progress * view.width * 0.15f + router.backstack[router.backstackSize - 2].controller.view?.let { view2 -> + view2.alpha = progress + view2.x = view.x - view.width * 0.2f + } + } + } + } + + @CallSuper + fun handleOnBackCancelled() { + if (this !is Controller) return + view?.alpha = 1f + view?.x = 0f + if (router.backstackSize > 1) { + router.backstack[router.backstackSize - 2].controller.view?.let { view -> + view.alpha = 0f + view.x = 0f + } + } + } +} diff --git a/app/src/main/res/layout/browse_source_controller.xml b/app/src/main/res/layout/browse_source_controller.xml index 9befc9d01c..99a0b76bb3 100644 --- a/app/src/main/res/layout/browse_source_controller.xml +++ b/app/src/main/res/layout/browse_source_controller.xml @@ -3,6 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" + android:background="?background" android:layout_width="match_parent" android:id="@+id/source_layout" android:layout_height="match_parent">