Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de007d17 authored by Matt Casey's avatar Matt Casey Committed by Android (Google) Code Review
Browse files

Merge "Switch screenshot dismissal to a spring animation." into main

parents 1f6e8853 b746f58c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -726,6 +726,16 @@ flag {
    bug: "362720389"
}

flag {
    name: "screenshot_dismissal_spring"
    namespace: "systemui"
    description: "Use a spring animator for screenshot dismissal, fade out after const distance"
    bug: "412986001"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "screenshot_policy_split_and_desktop_mode"
    namespace: "systemui"
+2 −1
Original line number Diff line number Diff line
@@ -473,7 +473,8 @@

    <!-- Dimensions related to screenshots -->


    <!-- Fade out the screenshot UI over this distance when dismissing -->
    <dimen name="dismissal_max_distance_fadeout">250dp</dimen>
    <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
    <dimen name="long_screenshot_action_bar_top_margin">4dp</dimen>

+32 −12
Original line number Diff line number Diff line
@@ -33,12 +33,14 @@ import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowInsets
import android.view.WindowManager
import android.window.DesktopExperienceFlags
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags
import com.android.systemui.Flags.screenshotAnnounceLiveRegion
import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.res.R
@@ -187,6 +189,14 @@ constructor(
            return
        }
        event?.let { logger.log(it, 0, packageName) }

        if (SCREENSHOT_DISMISSAL_SPRING.isTrue()) {
            animationController.startDismissal(velocity) {
                isDismissing = false
                callbacks?.onDismiss()
            }
            isDismissing = true
        } else {
            val animator = animationController.getSwipeDismissAnimation(velocity)
            animator.addListener(
                object : AnimatorListenerAdapter() {
@@ -202,6 +212,7 @@ constructor(
            )
            animator.start()
        }
    }

    fun prepareScrollingTransition(
        response: ScrollCaptureResponse,
@@ -362,6 +373,15 @@ constructor(
        )
    }

    companion object {
        val SCREENSHOT_DISMISSAL_SPRING =
            DesktopExperienceFlags.DesktopExperienceFlag(
                Flags::screenshotDismissalSpring,
                /* shouldOverrideByDevOption= */ true,
                Flags.FLAG_SCREENSHOT_DISMISSAL_SPRING,
            )
    }

    @AssistedFactory
    interface Factory {
        fun getProxy(context: Context, displayId: Int): ScreenshotShelfViewProxy
+61 −4
Original line number Diff line number Diff line
@@ -31,8 +31,13 @@ import android.util.MathUtils
import android.view.View
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.window.DesktopExperienceFlags
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import com.android.internal.dynamicanimation.animation.DynamicAnimation
import com.android.internal.dynamicanimation.animation.SpringAnimation
import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.screenshot.scroll.ScrollCaptureController
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
@@ -45,6 +50,7 @@ class ScreenshotAnimationController(
    private val viewModel: ScreenshotViewModel,
) {
    private var animator: Animator? = null
    private var dismissalSpring: SpringAnimation? = null
    private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview)
    private val scrollingScrim = view.requireViewById<ImageView>((R.id.screenshot_scrolling_scrim))
    private val scrollTransitionPreview =
@@ -132,6 +138,7 @@ class ScreenshotAnimationController(

    fun fadeForSharedTransition() {
        animator?.cancel()
        dismissalSpring?.cancel()
        val fadeAnimator = ValueAnimator.ofFloat(1f, 0f)
        fadeAnimator.addUpdateListener {
            for (view in fadeUI) {
@@ -214,6 +221,7 @@ class ScreenshotAnimationController(
    }

    fun restoreUI() {
        dismissalSpring?.cancel()
        animator?.cancel()
        for (view in fadeUI) {
            view.alpha = 1f
@@ -229,6 +237,35 @@ class ScreenshotAnimationController(
        return animator
    }

    fun startDismissal(requestedVelocity: Float?, onEnd: () -> Unit) {
        dismissalSpring?.cancel()
        animator?.cancel()
        val velocity = getAdjustedVelocity(requestedVelocity)

        val maxDistanceFadeout =
            view.resources.getDimensionPixelOffset(R.dimen.dismissal_max_distance_fadeout).toFloat()
        val startingTranslation = view.translationX

        // Set the spring target to be a bit past the max translation distance
        val target = startingTranslation + maxDistanceFadeout * sign(velocity) * 1.2f

        dismissalSpring =
            SpringAnimation(view, DynamicAnimation.TRANSLATION_X, target).apply {
                setStartVelocity(velocity * 1000)
                setStartValue(startingTranslation)

                spring?.setStiffness(SpringForce.STIFFNESS_LOW)
                spring?.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
            }

        dismissalSpring?.addUpdateListener { _, value, _ ->
            view.alpha =
                MathUtils.lerp(1f, 0f, abs(value - startingTranslation) / maxDistanceFadeout)
        }
        dismissalSpring?.addEndListener { _, _, _, _ -> onEnd.invoke() }
        dismissalSpring?.start()
    }

    fun getSwipeDismissAnimation(requestedVelocity: Float?): Animator {
        animator?.cancel()
        val velocity = getAdjustedVelocity(requestedVelocity)
@@ -247,6 +284,7 @@ class ScreenshotAnimationController(
            view.translationX = it.animatedValue as Float
            view.alpha = 1f - it.animatedFraction
        }

        animator.duration = ((abs(distance / velocity))).toLong()
        animator.doOnStart { viewModel.setIsAnimating(true) }
        animator.doOnEnd { viewModel.setIsAnimating(false) }
@@ -257,6 +295,7 @@ class ScreenshotAnimationController(

    fun cancel() {
        animator?.cancel()
        dismissalSpring?.cancel()
    }

    private fun getActionsAnimator(): Animator {
@@ -325,21 +364,39 @@ class ScreenshotAnimationController(

    private fun getAdjustedVelocity(requestedVelocity: Float?): Float {
        return if (requestedVelocity == null || abs(requestedVelocity) < .005f) {
            val isLTR = view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
            if (SCREENSHOT_DISMISSAL_SPRING.isTrue) {
                if (viewClosestToLeftEdge()) -MINIMUM_VELOCITY else MINIMUM_VELOCITY
            } else {
                val isLTR =
                    view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
                // dismiss to the left in LTR locales, to the right in RTL
                if (isLTR) -MINIMUM_VELOCITY else MINIMUM_VELOCITY
            }
        } else {
            sign(requestedVelocity) * max(MINIMUM_VELOCITY, abs(requestedVelocity))
        }
    }

    private fun viewClosestToLeftEdge(): Boolean {
        var rect = Rect()
        view.getBoundsOnScreen(rect)
        return rect.centerX() < view.resources.displayMetrics.widthPixels / 2f
    }

    companion object {
        private const val TAG = "ScreenshotAnimationController"
        private const val MINIMUM_VELOCITY = 1.5f // pixels per second
        private const val MINIMUM_VELOCITY = 1.5f // pixels per millisecond
        private const val FLASH_IN_DURATION_MS: Long = 133
        private const val FLASH_OUT_DURATION_MS: Long = 217
        private const val PREVIEW_X_ANIMATION_DURATION_MS: Long = 234
        private const val PREVIEW_Y_ANIMATION_DURATION_MS: Long = 500
        private const val ACTION_REVEAL_DELAY_MS: Long = 200

        val SCREENSHOT_DISMISSAL_SPRING =
            DesktopExperienceFlags.DesktopExperienceFlag(
                Flags::screenshotDismissalSpring,
                /* shouldOverrideByDevOption= */ true,
                Flags.FLAG_SCREENSHOT_DISMISSAL_SPRING,
            )
    }
}