mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Replace Resume FAB reveal animation with container transform (#6250)
This commit is contained in:
		@@ -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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user