From bdef2cfdfb50800c35c028a7d41343a99bf95f60 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Fri, 19 Nov 2021 22:16:39 +0700 Subject: [PATCH] Replace Resume FAB reveal animation with container transform (#6250) --- app/build.gradle.kts | 2 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 8 ++ .../tachiyomi/ui/manga/MangaController.kt | 47 ++++-------- .../tachiyomi/ui/reader/ReaderActivity.kt | 25 +++++++ .../tachiyomi/widget/RevealAnimationView.kt | 73 ------------------- .../res/layout-sw720dp/manga_controller.xml | 8 -- app/src/main/res/layout/manga_controller.xml | 8 -- 7 files changed, 48 insertions(+), 123 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2983289cf4..8aefa99df7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -244,7 +244,7 @@ dependencies { implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0") { exclude(group = "androidx.viewpager", module = "viewpager") } - implementation("dev.chrisbanes.insetter:insetter:0.6.0") + implementation("dev.chrisbanes.insetter:insetter:0.6.1") // Conductor val conductorVersion = "3.0.0" 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 435fcf73c2..5048fe4131 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 @@ -8,6 +8,7 @@ import android.os.Build import android.os.Bundle import android.view.Gravity import android.view.ViewGroup +import android.view.Window import android.widget.Toast import androidx.appcompat.view.ActionMode import androidx.core.animation.doOnEnd @@ -28,6 +29,7 @@ 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 import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.Migrations @@ -99,6 +101,11 @@ class MainActivity : BaseViewBindingActivity() { // Prevent splash screen showing up on configuration changes val splashScreen = if (savedInstanceState == null) installSplashScreen() else null + // Set up shared element transition and disable overlay so views don't show above system bars + window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) + setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback()) + window.sharedElementsUseOverlay = false + super.onCreate(savedInstanceState) val didMigration = if (savedInstanceState == null) Migrations.upgrade(preferences) else false @@ -117,6 +124,7 @@ class MainActivity : BaseViewBindingActivity() { // Draw edge-to-edge WindowCompat.setDecorFitsSystemWindows(window, false) binding.fabLayout.rootFab.applyInsetter { + ignoreVisibility(true) type(navigationBars = true) { margin() } 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 d1628f3cf2..60fb4a9e39 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 @@ -1,8 +1,7 @@ package eu.kanade.tachiyomi.ui.manga -import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.app.Activity +import android.app.ActivityOptions import android.content.Context import android.content.Intent import android.graphics.Bitmap @@ -90,7 +89,6 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast -import eu.kanade.tachiyomi.util.view.getCoordinates import eu.kanade.tachiyomi.util.view.shrinkOnScroll import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView @@ -369,18 +367,7 @@ class MangaController : fab.setOnClickListener { val item = presenter.getNextUnreadChapter() if (item != null) { - // Get coordinates and start animation - actionFab?.getCoordinates()?.let { coordinates -> - binding.revealView.showRevealEffect( - coordinates.x, - coordinates.y, - object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator?) { - openChapter(item.chapter, true) - } - } - ) - } + openChapter(item.chapter, it) } } } @@ -413,20 +400,6 @@ class MangaController : super.onDestroyView(view) } - override fun onActivityResumed(activity: Activity) { - if (view == null) return - - // Check if animation view is visible - if (binding.revealView.isVisible) { - // Show the unreveal effect - actionFab?.getCoordinates()?.let { coordinates -> - binding.revealView.hideRevealEffect(coordinates.x, coordinates.y, 1920) - } - } - - super.onActivityResumed(activity) - } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.manga, menu) } @@ -914,13 +887,21 @@ class MangaController : } } - fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) { + private fun openChapter(chapter: Chapter, sharedElement: View? = null) { val activity = activity ?: return val intent = ReaderActivity.newIntent(activity, presenter.manga, chapter) - if (hasAnimation) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) + activity.apply { + if (sharedElement != null) { + val activityOptions = ActivityOptions.makeSceneTransitionAnimation( + activity, + sharedElement, + ReaderActivity.SHARED_ELEMENT_NAME + ) + startActivity(intent, activityOptions.toBundle()) + } else { + startActivity(intent) + } } - startActivity(intent) } override fun onItemClick(view: View?, position: Int): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 45979cfdf1..82ce65c288 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -22,7 +22,9 @@ import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.MotionEvent +import android.view.View import android.view.View.LAYER_TYPE_HARDWARE +import android.view.Window import android.view.WindowManager import android.view.animation.Animation import android.view.animation.AnimationUtils @@ -39,6 +41,8 @@ import androidx.lifecycle.lifecycleScope import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.slider.Slider +import com.google.android.material.transition.platform.MaterialContainerTransform +import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import dev.chrisbanes.insetter.applyInsetter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Chapter @@ -105,6 +109,8 @@ class ReaderActivity : BaseRxActivity() private const val ENABLED_BUTTON_IMAGE_ALPHA = 255 private const val DISABLED_BUTTON_IMAGE_ALPHA = 64 + + const val SHARED_ELEMENT_NAME = "reader_shared_element_root" } private val preferences: PreferencesHelper by injectLazy() @@ -150,6 +156,17 @@ class ReaderActivity : BaseRxActivity() */ override fun onCreate(savedInstanceState: Bundle?) { applyAppTheme(preferences) + + // Setup shared element transitions + window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) + findViewById(android.R.id.content).transitionName = SHARED_ELEMENT_NAME + setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback()) + window.sharedElementEnterTransition = buildContainerTransform(true) + window.sharedElementReturnTransition = buildContainerTransform(false) + + // Postpone custom transition until manga ready + postponeEnterTransition() + super.onCreate(savedInstanceState) binding = ReaderActivityBinding.inflate(layoutInflater) @@ -295,6 +312,12 @@ class ReaderActivity : BaseRxActivity() return handled || super.dispatchGenericMotionEvent(event) } + private fun buildContainerTransform(entering: Boolean): MaterialContainerTransform { + return MaterialContainerTransform(this, entering).apply { + addTarget(android.R.id.content) + } + } + /** * Initializes the reader menu. It sets up click listeners and the initial visibility. */ @@ -613,6 +636,8 @@ class ReaderActivity : BaseRxActivity() } } binding.readerContainer.addView(loadingIndicator) + + startPostponedEnterTransition() } private fun showReadingModeToast(mode: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt deleted file mode 100644 index 6bd1546f19..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/RevealAnimationView.kt +++ /dev/null @@ -1,73 +0,0 @@ -package eu.kanade.tachiyomi.widget - -import android.animation.Animator -import android.content.Context -import android.util.AttributeSet -import android.view.View -import android.view.ViewAnimationUtils -import androidx.core.animation.doOnEnd -import androidx.core.view.isInvisible -import androidx.core.view.isVisible - -class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - View(context, attrs) { - - /** - * Hides the animation view with a animation - * - * @param centerX x starting point - * @param centerY y starting point - * @param initialRadius size of radius of animation - */ - fun hideRevealEffect(centerX: Int, centerY: Int, initialRadius: Int) { - // Make the view visible. - this.isVisible = true - - // Create the animation (the final radius is zero). - val anim = ViewAnimationUtils.createCircularReveal( - this, - centerX, - centerY, - initialRadius.toFloat(), - 0f - ) - - // Set duration of animation. - anim.duration = 500 - - // make the view invisible when the animation is done - anim.doOnEnd { - this@RevealAnimationView.isInvisible = true - } - - anim.start() - } - - /** - * Fills the animation view with a animation - * - * @param centerX x starting point - * @param centerY y starting point - * @param listener animation listener - */ - fun showRevealEffect(centerX: Int, centerY: Int, listener: Animator.AnimatorListener) { - this.isVisible = true - - val height = this.height - - // Create animation - val anim = ViewAnimationUtils.createCircularReveal( - this, - centerX, - centerY, - 0f, - height.toFloat() - ) - - // Set duration of animation - anim.duration = 350 - - anim.addListener(listener) - anim.start() - } -} diff --git a/app/src/main/res/layout-sw720dp/manga_controller.xml b/app/src/main/res/layout-sw720dp/manga_controller.xml index 3a727142b2..e4fa42b147 100644 --- a/app/src/main/res/layout-sw720dp/manga_controller.xml +++ b/app/src/main/res/layout-sw720dp/manga_controller.xml @@ -6,14 +6,6 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - -