Merge "[Media TTT] Add animation out for receiver chip" into tm-qpr-dev

This commit is contained in:
Michael Mikhail
2022-11-18 00:52:52 +00:00
committed by Android (Google) Code Review
5 changed files with 130 additions and 8 deletions

View File

@@ -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?,

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -114,7 +114,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
controllerReceiver = MediaTttChipControllerReceiver(
controllerReceiver = FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,