Merge "[Media TTT] Apply ripple effect for new states" into tm-qpr-dev
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 f55fb97..9058510 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 @@
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 @@
* 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 ae28a8b..b37c734 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 @@
*/
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 fbf413b..1f80b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -315,6 +315,10 @@
// 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 03bc935..8a565fa 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 @@
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 358534c..889147b 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
@@ -114,6 +114,9 @@
}
}
+ private var maxRippleWidth: Float = 0f
+ private var maxRippleHeight: Float = 0f
+
private fun updateMediaTapToTransferReceiverDisplay(
@StatusBarManager.MediaTransferReceiverState displayState: Int,
routeInfo: MediaRoute2Info,
@@ -216,7 +219,7 @@
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())
@@ -226,7 +229,14 @@
.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) {
@@ -271,12 +281,19 @@
})
}
- 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)
@@ -286,6 +303,11 @@
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 6e9fc5c..87b2528 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.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 @@
})
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 532fbaa..ad48e21 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -331,7 +331,7 @@
return
}
- removeViewFromWindow(displayInfo)
+ removeViewFromWindow(displayInfo, removalReason)
// Prune anything that's already timed out before determining if we should re-display a
// different chipbar.
@@ -358,14 +358,14 @@
removeViewFromWindow(displayInfo)
}
- private fun removeViewFromWindow(displayInfo: DisplayInfo) {
+ private fun removeViewFromWindow(displayInfo: DisplayInfo, removalReason: String? = null) {
val view = displayInfo.view
if (view == null) {
logger.logViewRemovalIgnored(displayInfo.info.id, "View is null")
return
}
displayInfo.view = null // Need other places??
- animateViewOut(view) {
+ animateViewOut(view, removalReason) {
windowManager.removeView(view)
displayInfo.wakeLock?.release(displayInfo.info.wakeReason)
}
@@ -428,7 +428,11 @@
*
* @param onAnimationEnd an action that *must* be run once the animation finishes successfully.
*/
- internal open fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
+ internal open fun animateViewOut(
+ view: ViewGroup,
+ removalReason: String? = null,
+ onAnimationEnd: Runnable
+ ) {
onAnimationEnd.run()
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index fd2c705..52980c3 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -211,7 +211,7 @@
)
}
- override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
+ override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
val innerView = view.getInnerView()
innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
ViewHierarchyAnimator.animateRemoval(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index 07a3109..9c4e849 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -66,7 +66,7 @@
wakeLockBuilder,
systemClock,
) {
- override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
+ override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
// Just bypass the animation in tests
onAnimationEnd.run()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 03ba3d3..cefc742 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -98,6 +98,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
+ whenever(mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()).thenReturn(true)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
index b9a5bd7..4ef4e6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
@@ -64,7 +64,7 @@
wakeLockBuilder,
systemClock,
) {
- override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
+ override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
// Just bypass the animation in tests
onAnimationEnd.run()
}