mirror of
https://git.mihon.tech/mihonapp/mihon
synced 2024-11-26 15:16:04 +03:00
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") {
|
implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0") {
|
||||||
exclude(group = "androidx.viewpager", module = "viewpager")
|
exclude(group = "androidx.viewpager", module = "viewpager")
|
||||||
}
|
}
|
||||||
implementation("dev.chrisbanes.insetter:insetter:0.6.0")
|
implementation("dev.chrisbanes.insetter:insetter:0.6.1")
|
||||||
|
|
||||||
// Conductor
|
// Conductor
|
||||||
val conductorVersion = "3.0.0"
|
val conductorVersion = "3.0.0"
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.Window
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
|
@ -28,6 +29,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||||
import com.bluelinelabs.conductor.Router
|
import com.bluelinelabs.conductor.Router
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
|
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
||||||
import dev.chrisbanes.insetter.applyInsetter
|
import dev.chrisbanes.insetter.applyInsetter
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.Migrations
|
import eu.kanade.tachiyomi.Migrations
|
||||||
|
@ -99,6 +101,11 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
|
||||||
// Prevent splash screen showing up on configuration changes
|
// Prevent splash screen showing up on configuration changes
|
||||||
val splashScreen = if (savedInstanceState == null) installSplashScreen() else null
|
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)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val didMigration = if (savedInstanceState == null) Migrations.upgrade(preferences) else false
|
val didMigration = if (savedInstanceState == null) Migrations.upgrade(preferences) else false
|
||||||
|
@ -117,6 +124,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
|
||||||
// Draw edge-to-edge
|
// Draw edge-to-edge
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
binding.fabLayout.rootFab.applyInsetter {
|
binding.fabLayout.rootFab.applyInsetter {
|
||||||
|
ignoreVisibility(true)
|
||||||
type(navigationBars = true) {
|
type(navigationBars = true) {
|
||||||
margin()
|
margin()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.ui.manga
|
package eu.kanade.tachiyomi.ui.manga
|
||||||
|
|
||||||
import android.animation.Animator
|
|
||||||
import android.animation.AnimatorListenerAdapter
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.ActivityOptions
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
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.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
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.shrinkOnScroll
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
import eu.kanade.tachiyomi.util.view.snack
|
||||||
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||||
|
@ -369,18 +367,7 @@ class MangaController :
|
||||||
fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
val item = presenter.getNextUnreadChapter()
|
val item = presenter.getNextUnreadChapter()
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
// Get coordinates and start animation
|
openChapter(item.chapter, it)
|
||||||
actionFab?.getCoordinates()?.let { coordinates ->
|
|
||||||
binding.revealView.showRevealEffect(
|
|
||||||
coordinates.x,
|
|
||||||
coordinates.y,
|
|
||||||
object : AnimatorListenerAdapter() {
|
|
||||||
override fun onAnimationStart(animation: Animator?) {
|
|
||||||
openChapter(item.chapter, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,20 +400,6 @@ class MangaController :
|
||||||
super.onDestroyView(view)
|
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) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.manga, menu)
|
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 activity = activity ?: return
|
||||||
val intent = ReaderActivity.newIntent(activity, presenter.manga, chapter)
|
val intent = ReaderActivity.newIntent(activity, presenter.manga, chapter)
|
||||||
if (hasAnimation) {
|
activity.apply {
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
|
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 {
|
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||||
|
|
|
@ -22,7 +22,9 @@ import android.view.KeyEvent
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
import android.view.View.LAYER_TYPE_HARDWARE
|
import android.view.View.LAYER_TYPE_HARDWARE
|
||||||
|
import android.view.Window
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.animation.Animation
|
import android.view.animation.Animation
|
||||||
import android.view.animation.AnimationUtils
|
import android.view.animation.AnimationUtils
|
||||||
|
@ -39,6 +41,8 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import com.google.android.material.slider.Slider
|
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 dev.chrisbanes.insetter.applyInsetter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
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 ENABLED_BUTTON_IMAGE_ALPHA = 255
|
||||||
private const val DISABLED_BUTTON_IMAGE_ALPHA = 64
|
private const val DISABLED_BUTTON_IMAGE_ALPHA = 64
|
||||||
|
|
||||||
|
const val SHARED_ELEMENT_NAME = "reader_shared_element_root"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val preferences: PreferencesHelper by injectLazy()
|
private val preferences: PreferencesHelper by injectLazy()
|
||||||
|
@ -150,6 +156,17 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
|
||||||
*/
|
*/
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
applyAppTheme(preferences)
|
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)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
binding = ReaderActivityBinding.inflate(layoutInflater)
|
binding = ReaderActivityBinding.inflate(layoutInflater)
|
||||||
|
@ -295,6 +312,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
|
||||||
return handled || super.dispatchGenericMotionEvent(event)
|
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.
|
* 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)
|
binding.readerContainer.addView(loadingIndicator)
|
||||||
|
|
||||||
|
startPostponedEnterTransition()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showReadingModeToast(mode: Int) {
|
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:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
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
|
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
|
||||||
android:id="@+id/swipe_refresh"
|
android:id="@+id/swipe_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -6,14 +6,6 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
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
|
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
|
||||||
android:id="@+id/swipe_refresh"
|
android:id="@+id/swipe_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
Loading…
Reference in a new issue