Replace Resume FAB reveal animation with container transform (#6250)

This commit is contained in:
Ivan Iskandar 2021-11-19 22:16:39 +07:00 committed by GitHub
parent f229a5e2ec
commit bdef2cfdfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 123 deletions

View file

@ -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"

View file

@ -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<MainActivityBinding>() {
// 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<MainActivityBinding>() {
// Draw edge-to-edge
WindowCompat.setDecorFitsSystemWindows(window, false)
binding.fabLayout.rootFab.applyInsetter {
ignoreVisibility(true)
type(navigationBars = true) {
margin()
}

View file

@ -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 {

View file

@ -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<ReaderActivityBinding, ReaderPresenter>()
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<ReaderActivityBinding, ReaderPresenter>()
*/
override fun onCreate(savedInstanceState: Bundle?) {
applyAppTheme(preferences)
// Setup shared element transitions
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
findViewById<View>(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<ReaderActivityBinding, ReaderPresenter>()
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<ReaderActivityBinding, ReaderPresenter>()
}
}
binding.readerContainer.addView(loadingIndicator)
startPostponedEnterTransition()
}
private fun showReadingModeToast(mode: Int) {

View file

@ -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()
}
}

View file

@ -6,14 +6,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
<eu.kanade.tachiyomi.widget.RevealAnimationView
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorAccent"
android:elevation="5dp"
android:visibility="invisible" />
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"

View file

@ -6,14 +6,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
<eu.kanade.tachiyomi.widget.RevealAnimationView
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorAccent"
android:elevation="5dp"
android:visibility="invisible" />
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"