Merge "[Media TTT] Add animation out for receiver chip" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9391ef81b5
@@ -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?,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
fakeWakeLockBuilder = WakeLockFake.Builder(context)
|
||||
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
|
||||
|
||||
controllerReceiver = MediaTttChipControllerReceiver(
|
||||
controllerReceiver = FakeMediaTttChipControllerReceiver(
|
||||
commandQueue,
|
||||
context,
|
||||
logger,
|
||||
|
||||
Reference in New Issue
Block a user