From be47aafb12973dc5592ee1402b2cb517450f6104 Mon Sep 17 00:00:00 2001 From: Michael Mikhail Date: Wed, 7 Dec 2022 21:21:08 +0000 Subject: [Media TTT] Apply ripple effect for new states Adds a new ripple effect when the transfer succeeded. The ripple should glow all over the screen and vanish. Bug: 257301489 Test: atest MediaTttChipControllerReceiverTest Test: Checked the UI is animating as intended. Change-Id: I66967487ce88a41316fbc83d2621e59a8aa7c168 --- .../systemui/surfaceeffects/ripple/RippleShader.kt | 14 +++++--- .../systemui/surfaceeffects/ripple/RippleView.kt | 2 +- .../src/com/android/systemui/flags/Flags.kt | 4 +++ .../systemui/media/taptotransfer/MediaTttFlags.kt | 4 +++ .../receiver/MediaTttChipControllerReceiver.kt | 30 +++++++++++++--- .../receiver/ReceiverChipRippleView.kt | 41 ++++++++++++++++++++++ .../TemporaryViewDisplayController.kt | 12 ++++--- .../temporarydisplay/chipbar/ChipbarCoordinator.kt | 2 +- .../receiver/FakeMediaTttChipControllerReceiver.kt | 2 +- .../receiver/MediaTttChipControllerReceiverTest.kt | 1 + .../chipbar/FakeChipbarCoordinator.kt | 2 +- 11 files changed, 97 insertions(+), 17 deletions(-) diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt index f55fb97a9a97..90585109643a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt @@ -169,11 +169,9 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : setFloatUniform("in_progress", value) val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value) - setFloatUniform( - "in_size", - /* width= */ maxSize.x * curvedProg, - /* height= */ maxSize.y * curvedProg - ) + currentWidth = maxSize.x * curvedProg + currentHeight = maxSize.y * curvedProg + setFloatUniform("in_size", /* width= */ currentWidth, /* height= */ currentHeight) setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f) // radius should not exceed width and height values. setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * curvedProg) @@ -237,4 +235,10 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : * False for a ring effect. */ var rippleFill: Boolean = false + + var currentWidth: Float = 0f + private set + + var currentHeight: Float = 0f + private set } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt index ae28a8b6dc2e..b37c73417119 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt @@ -36,7 +36,7 @@ import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape */ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { - private lateinit var rippleShader: RippleShader + protected lateinit var rippleShader: RippleShader lateinit var rippleShape: RippleShape private set diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index efb513d57db6..e560b55c7819 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -291,6 +291,10 @@ object Flags { // TODO(b/261734857): Tracking Bug @JvmField val UMO_TURBULENCE_NOISE = unreleasedFlag(909, "umo_turbulence_noise") + // TODO(b/263272731): Tracking Bug + val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = + unreleasedFlag(910, "media_ttt_receiver_success_ripple", teamfood = true) + // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging") diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt index 03bc9350674b..8a565fa86b35 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt @@ -26,4 +26,8 @@ import javax.inject.Inject class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags) { /** */ fun isMediaTttEnabled(): Boolean = featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER) + + /** Check whether the flag for the receiver success state is enabled. */ + fun isMediaTttReceiverSuccessRippleEnabled(): Boolean = + featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE) } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 7b9d0b4205af..5deed358bd90 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -111,6 +111,9 @@ open class MediaTttChipControllerReceiver @Inject constructor( } } + private var maxRippleWidth: Float = 0f + private var maxRippleHeight: Float = 0f + private fun updateMediaTapToTransferReceiverDisplay( @StatusBarManager.MediaTransferReceiverState displayState: Int, routeInfo: MediaRoute2Info, @@ -212,7 +215,7 @@ open class MediaTttChipControllerReceiver @Inject constructor( expandRipple(view.requireViewById(R.id.ripple)) } - override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) { + override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) { val appIconView = view.getAppIconView() appIconView.animate() .translationYBy(getTranslationAmount().toFloat()) @@ -222,7 +225,14 @@ open class MediaTttChipControllerReceiver @Inject constructor( .alpha(0f) .setDuration(ICON_ALPHA_ANIM_DURATION) .start() - (view.requireViewById(R.id.ripple) as ReceiverChipRippleView).collapseRipple(onAnimationEnd) + + val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple) + if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name && + mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) { + expandRippleToFull(rippleView, onAnimationEnd) + } else { + rippleView.collapseRipple(onAnimationEnd) + } } override fun getTouchableRegion(view: View, outRect: Rect) { @@ -267,12 +277,19 @@ open class MediaTttChipControllerReceiver @Inject constructor( }) } - private fun layoutRipple(rippleView: ReceiverChipRippleView) { + private fun layoutRipple(rippleView: ReceiverChipRippleView, isFullScreen: Boolean = false) { val windowBounds = windowManager.currentWindowMetrics.bounds val height = windowBounds.height().toFloat() val width = windowBounds.width().toFloat() - rippleView.setMaxSize(width / 2f, height / 2f) + if (isFullScreen) { + maxRippleHeight = height * 2f + maxRippleWidth = width * 2f + } else { + maxRippleHeight = height / 2f + maxRippleWidth = width / 2f + } + rippleView.setMaxSize(maxRippleWidth, maxRippleHeight) // Center the ripple on the bottom of the screen in the middle. rippleView.setCenter(width * 0.5f, height) val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent) @@ -282,6 +299,11 @@ open class MediaTttChipControllerReceiver @Inject constructor( private fun View.getAppIconView(): CachingIconView { return this.requireViewById(R.id.app_icon) } + + private fun expandRippleToFull(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable?) { + layoutRipple(rippleView, true) + rippleView.expandToFull(maxRippleHeight, onAnimationEnd) + } } val ICON_TRANSLATION_ANIM_DURATION = 30.frames diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt index 6e9fc5c33940..87b2528f93d3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt @@ -22,6 +22,7 @@ import android.content.Context import android.util.AttributeSet import com.android.systemui.surfaceeffects.ripple.RippleShader import com.android.systemui.surfaceeffects.ripple.RippleView +import kotlin.math.pow /** * An expanding ripple effect for the media tap-to-transfer receiver chip. @@ -59,4 +60,44 @@ class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleVi }) animator.reverse() } + + // Expands the ripple to cover full screen. + fun expandToFull(newHeight: Float, onAnimationEnd: Runnable? = null) { + if (!isStarted) { + return + } + // Reset all listeners to animator. + animator.removeAllListeners() + animator.removeAllUpdateListeners() + + // Only show the outline as ripple expands and disappears when animation ends. + setRippleFill(false) + + val startingPercentage = calculateStartingPercentage(newHeight) + animator.addUpdateListener { updateListener -> + val now = updateListener.currentPlayTime + val progress = updateListener.animatedValue as Float + rippleShader.progress = startingPercentage + (progress * (1 - startingPercentage)) + rippleShader.distortionStrength = 1 - rippleShader.progress + rippleShader.pixelDensity = 1 - rippleShader.progress + rippleShader.time = now.toFloat() + invalidate() + } + animator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + animation?.let { visibility = GONE } + onAnimationEnd?.run() + isStarted = false + } + }) + animator.start() + } + + // Calculates the actual starting percentage according to ripple shader progress set method. + // Check calculations in [RippleShader.progress] + fun calculateStartingPercentage(newHeight: Float): Float { + val ratio = rippleShader.currentHeight / newHeight + val remainingPercentage = (1 - ratio).toDouble().pow(1 / 3.toDouble()).toFloat() + return 1 - remainingPercentage + } } diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt index db7315f311ac..17df72d9a1a1 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt @@ -321,7 +321,7 @@ abstract class TemporaryViewDisplayController