Replace Resume FAB reveal animation with container transform (#6250)
This commit is contained in:
parent
f229a5e2ec
commit
bdef2cfdfb
7 changed files with 48 additions and 123 deletions
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Reference in a new issue