diff options
5 files changed, 130 insertions, 8 deletions
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 691953aaba36..cc5e256c0956 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 @@ -56,7 +56,7 @@ import javax.inject.Inject * TODO(b/245610654): Re-name this to be MediaTttReceiverCoordinator. */ @SysUISingleton -class MediaTttChipControllerReceiver @Inject constructor( +open class MediaTttChipControllerReceiver @Inject constructor( private val commandQueue: CommandQueue, context: Context, @MediaTttReceiverLogger logger: MediaTttLogger, @@ -183,15 +183,28 @@ class MediaTttChipControllerReceiver @Inject constructor( val appIconView = view.getAppIconView() appIconView.animate() .translationYBy(-1 * getTranslationAmount().toFloat()) - .setDuration(30.frames) + .setDuration(ICON_TRANSLATION_ANIM_DURATION) .start() appIconView.animate() .alpha(1f) - .setDuration(5.frames) + .setDuration(ICON_ALPHA_ANIM_DURATION) .start() // Using withEndAction{} doesn't apply a11y focus when screen is unlocked. appIconView.postOnAnimation { view.requestAccessibilityFocus() } - startRipple(view.requireViewById(R.id.ripple)) + expandRipple(view.requireViewById(R.id.ripple)) + } + + override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) { + val appIconView = view.getAppIconView() + appIconView.animate() + .translationYBy(getTranslationAmount().toFloat()) + .setDuration(ICON_TRANSLATION_ANIM_DURATION) + .start() + appIconView.animate() + .alpha(0f) + .setDuration(ICON_ALPHA_ANIM_DURATION) + .start() + (view.requireViewById(R.id.ripple) as ReceiverChipRippleView).collapseRipple(onAnimationEnd) } override fun getTouchableRegion(view: View, outRect: Rect) { @@ -205,11 +218,22 @@ class MediaTttChipControllerReceiver @Inject constructor( return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation) } - private fun startRipple(rippleView: ReceiverChipRippleView) { + private fun expandRipple(rippleView: ReceiverChipRippleView) { if (rippleView.rippleInProgress()) { // Skip if ripple is still playing return } + + // In case the device orientation changes, we need to reset the layout. + rippleView.addOnLayoutChangeListener ( + View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ -> + if (v == null) return@OnLayoutChangeListener + + val layoutChangedRippleView = v as ReceiverChipRippleView + layoutRipple(layoutChangedRippleView) + layoutChangedRippleView.invalidate() + } + ) rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { override fun onViewDetachedFromWindow(view: View?) {} @@ -219,7 +243,7 @@ class MediaTttChipControllerReceiver @Inject constructor( } val attachedRippleView = view as ReceiverChipRippleView layoutRipple(attachedRippleView) - attachedRippleView.startRipple() + attachedRippleView.expandRipple() attachedRippleView.removeOnAttachStateChangeListener(this) } }) @@ -242,6 +266,9 @@ class MediaTttChipControllerReceiver @Inject constructor( } } +val ICON_TRANSLATION_ANIM_DURATION = 30.frames +val ICON_ALPHA_ANIM_DURATION = 5.frames + data class ChipReceiverInfo( val routeInfo: MediaRoute2Info, val appIconDrawableOverride: Drawable?, 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 1ea202582f83..6e9fc5c33940 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 @@ -16,6 +16,8 @@ package com.android.systemui.media.taptotransfer.receiver +import android.animation.Animator +import android.animation.AnimatorListenerAdapter import android.content.Context import android.util.AttributeSet import com.android.systemui.surfaceeffects.ripple.RippleShader @@ -25,10 +27,36 @@ import com.android.systemui.surfaceeffects.ripple.RippleView * An expanding ripple effect for the media tap-to-transfer receiver chip. */ class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) { + + // Indicates whether the ripple started expanding. + private var isStarted: Boolean + init { setupShader(RippleShader.RippleShape.ELLIPSE) setRippleFill(true) setSparkleStrength(0f) duration = 3000L + isStarted = false + } + + fun expandRipple(onAnimationEnd: Runnable? = null) { + isStarted = true + super.startRipple(onAnimationEnd) + } + + /** Used to animate out the ripple. No-op if the ripple was never started via [startRipple]. */ + fun collapseRipple(onAnimationEnd: Runnable? = null) { + if (!isStarted) { + return // Ignore if ripple is not started yet. + } + // Reset all listeners to animator. + animator.removeAllListeners() + animator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + onAnimationEnd?.run() + isStarted = false + } + }) + animator.reverse() } } diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt index 2ad824348794..ae28a8b6dc2e 100644 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt @@ -41,7 +41,7 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a private set private val ripplePaint = Paint() - private val animator = ValueAnimator.ofFloat(0f, 1f) + protected val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f) var duration: Long = 1750 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 new file mode 100644 index 000000000000..4aa982ed1609 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.taptotransfer.receiver + +import android.content.Context +import android.os.Handler +import android.os.PowerManager +import android.view.ViewGroup +import android.view.WindowManager +import android.view.accessibility.AccessibilityManager +import com.android.systemui.media.taptotransfer.MediaTttFlags +import com.android.systemui.media.taptotransfer.common.MediaTttLogger +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.view.ViewUtil +import com.android.systemui.util.wakelock.WakeLock + +class FakeMediaTttChipControllerReceiver( + commandQueue: CommandQueue, + context: Context, + logger: MediaTttLogger, + windowManager: WindowManager, + mainExecutor: DelayableExecutor, + accessibilityManager: AccessibilityManager, + configurationController: ConfigurationController, + powerManager: PowerManager, + mainHandler: Handler, + mediaTttFlags: MediaTttFlags, + uiEventLogger: MediaTttReceiverUiEventLogger, + viewUtil: ViewUtil, + wakeLockBuilder: WakeLock.Builder, +) : + MediaTttChipControllerReceiver( + commandQueue, + context, + logger, + windowManager, + mainExecutor, + accessibilityManager, + configurationController, + powerManager, + mainHandler, + mediaTttFlags, + uiEventLogger, + viewUtil, + wakeLockBuilder, + ) { + override fun animateViewOut(view: ViewGroup, 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 885cc54af7cb..23f7cdb45026 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 @@ -114,7 +114,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { fakeWakeLockBuilder = WakeLockFake.Builder(context) fakeWakeLockBuilder.setWakeLock(fakeWakeLock) - controllerReceiver = MediaTttChipControllerReceiver( + controllerReceiver = FakeMediaTttChipControllerReceiver( commandQueue, context, logger, |