Merge changes from topics "presubmit-am-7250e98934bc40cbaa91f4d3fe499617", "presubmit-am-a05bb8af1eb24cd390463ad38288dc31" into tm-dev
* changes: Rework system event animations to a federated model Don't transition to ongoing privacy dot if still animating
This commit is contained in:
@@ -37,7 +37,5 @@
|
||||
android:layout_gravity="center"
|
||||
android:minWidth="@dimen/ongoing_appops_chip_min_width"
|
||||
android:maxWidth="@dimen/ongoing_appops_chip_max_width"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
/>
|
||||
</com.android.systemui.privacy.OngoingPrivacyChip>
|
||||
@@ -941,6 +941,9 @@
|
||||
<!-- Three privacy items. This value must not be exceeded -->
|
||||
<dimen name="ongoing_appops_chip_max_width">76dp</dimen>
|
||||
<dimen name="ongoing_appops_dot_diameter">6dp</dimen>
|
||||
<dimen name="ongoing_appops_chip_min_animation_width">10dp</dimen>
|
||||
<dimen name="ongoing_appops_chip_animation_in_status_bar_translation_x">15dp</dimen>
|
||||
<dimen name="ongoing_appops_chip_animation_out_status_bar_translation_x">7dp</dimen>
|
||||
<!-- Total minimum padding to enforce to ensure that the dot can always show -->
|
||||
<dimen name="ongoing_appops_dot_min_padding">20dp</dimen>
|
||||
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package com.android.systemui.statusbar.events
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@@ -30,6 +33,7 @@ import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowController
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Controls the view for system event animations.
|
||||
@@ -38,7 +42,7 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
private val context: Context,
|
||||
private val statusBarWindowController: StatusBarWindowController,
|
||||
private val contentInsetsProvider: StatusBarContentInsetsProvider
|
||||
) : SystemStatusChipAnimationCallback {
|
||||
) : SystemStatusAnimationCallback {
|
||||
|
||||
private lateinit var animationWindowView: FrameLayout
|
||||
|
||||
@@ -49,90 +53,169 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
private var chipRight = 0
|
||||
private var chipLeft = 0
|
||||
private var chipWidth = 0
|
||||
private var dotCenter = Point(0, 0)
|
||||
private var chipMinWidth = context.resources.getDimensionPixelSize(
|
||||
R.dimen.ongoing_appops_chip_min_animation_width)
|
||||
private var dotSize = context.resources.getDimensionPixelSize(
|
||||
R.dimen.ongoing_appops_dot_diameter)
|
||||
// If the chip animates away to a persistent dot, then we modify the CHIP_OUT animation
|
||||
private var isAnimatingToDot = false
|
||||
// Use during animation so that multiple animators can update the drawing rect
|
||||
private var animRect = Rect()
|
||||
|
||||
// TODO: move to dagger
|
||||
private var initialized = false
|
||||
|
||||
override fun onChipAnimationStart(
|
||||
viewCreator: ViewCreator,
|
||||
@SystemAnimationState state: Int
|
||||
) {
|
||||
if (!initialized) init()
|
||||
/**
|
||||
* Give the chip controller a chance to inflate and configure the chip view before we start
|
||||
* animating
|
||||
*/
|
||||
fun prepareChipAnimation(viewCreator: ViewCreator) {
|
||||
if (!initialized) {
|
||||
init()
|
||||
}
|
||||
animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT
|
||||
|
||||
if (state == ANIMATING_IN) {
|
||||
animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT
|
||||
// Initialize the animated view
|
||||
val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
|
||||
currentAnimatedView = viewCreator(context).also {
|
||||
animationWindowView.addView(
|
||||
it.view,
|
||||
layoutParamsDefault(
|
||||
if (animationWindowView.isLayoutRtl) insets.first
|
||||
else insets.second))
|
||||
it.view.alpha = 0f
|
||||
// For some reason, the window view's measured width is always 0 here, so use the
|
||||
// parent (status bar)
|
||||
it.view.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(
|
||||
(animationWindowView.parent as View).width, AT_MOST),
|
||||
View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST))
|
||||
chipWidth = it.chipWidth
|
||||
}
|
||||
|
||||
// Initialize the animated view
|
||||
val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
|
||||
currentAnimatedView = viewCreator(context).also {
|
||||
animationWindowView.addView(
|
||||
it.view,
|
||||
layoutParamsDefault(
|
||||
if (animationWindowView.isLayoutRtl) insets.first
|
||||
else insets.second))
|
||||
it.view.alpha = 0f
|
||||
// For some reason, the window view's measured width is always 0 here, so use the
|
||||
// parent (status bar)
|
||||
it.view.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(
|
||||
(animationWindowView.parent as View).width, AT_MOST),
|
||||
View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST))
|
||||
chipWidth = it.chipWidth
|
||||
// decide which direction we're animating from, and then set some screen coordinates
|
||||
val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
|
||||
when (animationDirection) {
|
||||
LEFT -> {
|
||||
chipRight = contentRect.right
|
||||
chipLeft = contentRect.right - chipWidth
|
||||
}
|
||||
|
||||
// decide which direction we're animating from, and then set some screen coordinates
|
||||
val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
|
||||
when (animationDirection) {
|
||||
LEFT -> {
|
||||
chipRight = contentRect.right
|
||||
chipLeft = contentRect.right - chipWidth
|
||||
}
|
||||
else /* RIGHT */ -> {
|
||||
chipLeft = contentRect.left
|
||||
chipRight = contentRect.left + chipWidth
|
||||
}
|
||||
}
|
||||
|
||||
currentAnimatedView?.apply {
|
||||
updateAnimatedViewBoundsForAmount(0.1f, this)
|
||||
}
|
||||
} else {
|
||||
// We are animating away
|
||||
currentAnimatedView!!.view.apply {
|
||||
alpha = 1f
|
||||
else /* RIGHT */ -> {
|
||||
chipLeft = contentRect.left
|
||||
chipRight = contentRect.left + chipWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChipAnimationEnd(@SystemAnimationState state: Int) {
|
||||
if (state == ANIMATING_IN) {
|
||||
// Finished animating in
|
||||
currentAnimatedView?.apply {
|
||||
updateAnimatedViewBoundsForAmount(1f, this)
|
||||
}
|
||||
} else {
|
||||
// Finished animating away
|
||||
currentAnimatedView!!.view.apply {
|
||||
visibility = View.INVISIBLE
|
||||
}
|
||||
animationWindowView.removeView(currentAnimatedView!!.view)
|
||||
override fun onSystemEventAnimationBegin(): Animator {
|
||||
initializeAnimRect()
|
||||
|
||||
val alphaIn = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
startDelay = 117
|
||||
duration = 83
|
||||
interpolator = null
|
||||
addUpdateListener { currentAnimatedView?.view?.alpha = animatedValue as Float }
|
||||
}
|
||||
val moveIn = ValueAnimator.ofInt(chipMinWidth, chipWidth).apply {
|
||||
startDelay = 117
|
||||
duration = 383
|
||||
interpolator = STATUS_BAR_X_MOVE_IN
|
||||
addUpdateListener {
|
||||
updateAnimatedViewBoundsWidth(animatedValue as Int)
|
||||
}
|
||||
}
|
||||
val animSet = AnimatorSet()
|
||||
animSet.playTogether(alphaIn, moveIn)
|
||||
return animSet
|
||||
}
|
||||
|
||||
override fun onChipAnimationUpdate(
|
||||
animator: ValueAnimator,
|
||||
@SystemAnimationState state: Int
|
||||
) {
|
||||
currentAnimatedView?.apply {
|
||||
val amt = (animator.animatedValue as Float).amt()
|
||||
view.alpha = (animator.animatedValue as Float)
|
||||
updateAnimatedViewBoundsForAmount(amt, this)
|
||||
override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
|
||||
initializeAnimRect()
|
||||
val finish = if (hasPersistentDot) {
|
||||
createMoveOutAnimationForDot()
|
||||
} else {
|
||||
createMoveOutAnimationDefault()
|
||||
}
|
||||
|
||||
finish.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
animationWindowView.removeView(currentAnimatedView!!.view)
|
||||
}
|
||||
})
|
||||
|
||||
return finish
|
||||
}
|
||||
|
||||
private fun createMoveOutAnimationForDot(): Animator {
|
||||
val width1 = ValueAnimator.ofInt(chipWidth, chipMinWidth).apply {
|
||||
duration = 150
|
||||
interpolator = STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_1
|
||||
addUpdateListener {
|
||||
updateAnimatedViewBoundsWidth(it.animatedValue as Int)
|
||||
}
|
||||
}
|
||||
|
||||
val width2 = ValueAnimator.ofInt(chipMinWidth, dotSize).apply {
|
||||
startDelay = 150
|
||||
duration = 333
|
||||
interpolator = STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_2
|
||||
addUpdateListener {
|
||||
updateAnimatedViewBoundsWidth(it.animatedValue as Int)
|
||||
}
|
||||
}
|
||||
|
||||
val keyFrame1Height = dotSize * 2
|
||||
val v = currentAnimatedView!!.view
|
||||
val chipVerticalCenter = v.top + v.measuredHeight / 2
|
||||
val height1 = ValueAnimator.ofInt(
|
||||
currentAnimatedView!!.view.measuredHeight, keyFrame1Height).apply {
|
||||
startDelay = 133
|
||||
duration = 100
|
||||
interpolator = STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_1
|
||||
addUpdateListener {
|
||||
updateAnimatedViewBoundsHeight(it.animatedValue as Int, chipVerticalCenter)
|
||||
}
|
||||
}
|
||||
|
||||
val height2 = ValueAnimator.ofInt(keyFrame1Height, dotSize).apply {
|
||||
startDelay = 233
|
||||
duration = 250
|
||||
interpolator = STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_2
|
||||
addUpdateListener {
|
||||
updateAnimatedViewBoundsHeight(it.animatedValue as Int, chipVerticalCenter)
|
||||
}
|
||||
}
|
||||
|
||||
// Move the chip view to overlap exactly with the privacy dot. The chip displays by default
|
||||
// exactly adjacent to the dot, so we can just move over by the diameter of the dot itself
|
||||
val moveOut = ValueAnimator.ofInt(0, dotSize).apply {
|
||||
startDelay = 50
|
||||
duration = 183
|
||||
interpolator = STATUS_CHIP_MOVE_TO_DOT
|
||||
addUpdateListener {
|
||||
// If RTL, we can just invert the move
|
||||
val amt = if (animationDirection == LEFT) {
|
||||
animatedValue as Int
|
||||
} else {
|
||||
-(animatedValue as Int)
|
||||
}
|
||||
updateAnimatedBoundsX(amt)
|
||||
}
|
||||
}
|
||||
|
||||
val animSet = AnimatorSet()
|
||||
animSet.playTogether(width1, width2, height1, height2, moveOut)
|
||||
return animSet
|
||||
}
|
||||
|
||||
private fun createMoveOutAnimationDefault(): Animator {
|
||||
val moveOut = ValueAnimator.ofInt(chipWidth, chipMinWidth).apply {
|
||||
duration = 383
|
||||
addUpdateListener {
|
||||
currentAnimatedView?.apply {
|
||||
updateAnimatedViewBoundsWidth(it.animatedValue as Int)
|
||||
}
|
||||
}
|
||||
}
|
||||
return moveOut
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
@@ -144,7 +227,6 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
statusBarWindowController.addViewToWindow(animationWindowView, lp)
|
||||
animationWindowView.clipToPadding = false
|
||||
animationWindowView.clipChildren = false
|
||||
animationWindowView.measureAllChildren = true
|
||||
}
|
||||
|
||||
private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams =
|
||||
@@ -153,29 +235,55 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
it.marginEnd = marginEnd
|
||||
}
|
||||
|
||||
private fun updateAnimatedViewBoundsForAmount(amt: Float, chip: BackgroundAnimatableView) {
|
||||
private fun initializeAnimRect() = animRect.set(
|
||||
chipLeft,
|
||||
currentAnimatedView!!.view.top,
|
||||
chipRight,
|
||||
currentAnimatedView!!.view.bottom)
|
||||
|
||||
/**
|
||||
* To be called during an animation, sets the width and updates the current animated chip view
|
||||
*/
|
||||
private fun updateAnimatedViewBoundsWidth(width: Int) {
|
||||
when (animationDirection) {
|
||||
LEFT -> {
|
||||
chip.setBoundsForAnimation(
|
||||
(chipRight - (chipWidth * amt)).toInt(),
|
||||
chip.view.top,
|
||||
chipRight,
|
||||
chip.view.bottom)
|
||||
}
|
||||
else /* RIGHT */ -> {
|
||||
chip.setBoundsForAnimation(
|
||||
chipLeft,
|
||||
chip.view.top,
|
||||
(chipLeft + (chipWidth * amt)).toInt(),
|
||||
chip.view.bottom)
|
||||
animRect.set((chipRight - width), animRect.top, chipRight, animRect.bottom)
|
||||
} else /* RIGHT */ -> {
|
||||
animRect.set(chipLeft, animRect.top, (chipLeft + width), animRect.bottom)
|
||||
}
|
||||
}
|
||||
|
||||
updateCurrentAnimatedView()
|
||||
}
|
||||
|
||||
private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
|
||||
private fun right() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().right
|
||||
private fun left() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().left
|
||||
private fun Float.amt() = 0.01f.coerceAtLeast(this)
|
||||
/**
|
||||
* To be called during an animation, updates the animation rect and sends the update to the chip
|
||||
*/
|
||||
private fun updateAnimatedViewBoundsHeight(height: Int, verticalCenter: Int) {
|
||||
animRect.set(
|
||||
animRect.left,
|
||||
verticalCenter - (height.toFloat() / 2).roundToInt(),
|
||||
animRect.right,
|
||||
verticalCenter + (height.toFloat() / 2).roundToInt())
|
||||
|
||||
updateCurrentAnimatedView()
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called during an animation, updates the animation rect offset and updates the chip
|
||||
*/
|
||||
private fun updateAnimatedBoundsX(translation: Int) {
|
||||
currentAnimatedView?.view?.translationX = translation.toFloat()
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called during an animation. Sets the chip rect to animRect
|
||||
*/
|
||||
private fun updateCurrentAnimatedView() {
|
||||
currentAnimatedView?.setBoundsForAnimation(
|
||||
animRect.left, animRect.top, animRect.right, animRect.bottom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,14 +19,12 @@ package com.android.systemui.statusbar.events
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.IntDef
|
||||
import android.os.Process
|
||||
import android.provider.DeviceConfig
|
||||
import android.util.Log
|
||||
import android.view.animation.PathInterpolator
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.animation.Interpolators.STANDARD_ACCELERATE
|
||||
import com.android.systemui.animation.Interpolators.STANDARD_DECELERATE
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
@@ -38,6 +36,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.time.SystemClock
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -116,7 +115,10 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
scheduledEvent?.updateFromEvent(event)
|
||||
if (event.forceVisible) {
|
||||
hasPersistentDot = true
|
||||
notifyTransitionToPersistentDot()
|
||||
// If we missed the chance to show the persistent dot, do it now
|
||||
if (animationState == IDLE) {
|
||||
notifyTransitionToPersistentDot()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
@@ -162,68 +164,90 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
// Schedule the animation to start after a debounce period
|
||||
cancelExecutionRunnable = executor.executeDelayed({
|
||||
cancelExecutionRunnable = null
|
||||
animationState = ANIMATING_IN
|
||||
statusBarWindowController.setForceStatusBarVisible(true)
|
||||
chipAnimationController.prepareChipAnimation(scheduledEvent!!.viewCreator)
|
||||
animationState = ANIMATION_QUEUED
|
||||
executor.executeDelayed({
|
||||
runChipAnimation()
|
||||
}, DEBOUNCE_DELAY)
|
||||
}
|
||||
|
||||
val entranceAnimator = ValueAnimator.ofFloat(1f, 0f)
|
||||
entranceAnimator.duration = ENTRANCE_ANIM_LENGTH
|
||||
entranceAnimator.addListener(systemAnimatorAdapter)
|
||||
entranceAnimator.addUpdateListener(systemUpdateListener)
|
||||
entranceAnimator.interpolator = STANDARD_ACCELERATE
|
||||
/**
|
||||
* 1. Define a total budget for the chip animation (1500ms)
|
||||
* 2. Send out callbacks to listeners so that they can generate animations locally
|
||||
* 3. Update the scheduler state so that clients know where we are
|
||||
* 4. Maybe: provide scaffolding such as: dot location, margins, etc
|
||||
* 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we
|
||||
* collect all of the animators and run them together.
|
||||
*/
|
||||
private fun runChipAnimation() {
|
||||
statusBarWindowController.setForceStatusBarVisible(true)
|
||||
animationState = ANIMATING_IN
|
||||
|
||||
val chipAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||
chipAnimator.duration = CHIP_ANIM_LENGTH
|
||||
chipAnimator.addListener(
|
||||
ChipAnimatorAdapter(RUNNING_CHIP_ANIM, scheduledEvent!!.viewCreator))
|
||||
chipAnimator.addUpdateListener(chipUpdateListener)
|
||||
chipAnimator.interpolator = STANDARD_DECELERATE
|
||||
val animSet = collectStartAnimations()
|
||||
if (animSet.totalDuration > 500) {
|
||||
throw IllegalStateException("System animation total length exceeds budget. " +
|
||||
"Expected: 500, actual: ${animSet.totalDuration}")
|
||||
}
|
||||
animSet.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
animationState = RUNNING_CHIP_ANIM
|
||||
}
|
||||
})
|
||||
animSet.start()
|
||||
|
||||
val aSet2 = AnimatorSet()
|
||||
aSet2.playSequentially(entranceAnimator, chipAnimator)
|
||||
aSet2.start()
|
||||
|
||||
executor.executeDelayed({
|
||||
animationState = ANIMATING_OUT
|
||||
|
||||
val systemAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||
systemAnimator.duration = ENTRANCE_ANIM_LENGTH
|
||||
systemAnimator.addListener(systemAnimatorAdapter)
|
||||
systemAnimator.addUpdateListener(systemUpdateListener)
|
||||
systemAnimator.interpolator = STANDARD_DECELERATE
|
||||
systemAnimator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
statusBarWindowController.setForceStatusBarVisible(false)
|
||||
executor.executeDelayed({
|
||||
val animSet2 = collectFinishAnimations()
|
||||
animationState = ANIMATING_OUT
|
||||
animSet2.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
animationState = if (hasPersistentDot) {
|
||||
SHOWING_PERSISTENT_DOT
|
||||
} else {
|
||||
IDLE
|
||||
}
|
||||
})
|
||||
|
||||
val chipAnimator = ValueAnimator.ofFloat(1f, 0f)
|
||||
chipAnimator.duration = CHIP_ANIM_LENGTH
|
||||
val endState = if (hasPersistentDot) {
|
||||
SHOWING_PERSISTENT_DOT
|
||||
} else {
|
||||
IDLE
|
||||
statusBarWindowController.setForceStatusBarVisible(false)
|
||||
}
|
||||
chipAnimator.addListener(
|
||||
ChipAnimatorAdapter(endState, scheduledEvent!!.viewCreator))
|
||||
chipAnimator.addUpdateListener(chipUpdateListener)
|
||||
chipAnimator.interpolator = STANDARD_ACCELERATE
|
||||
})
|
||||
animSet2.start()
|
||||
scheduledEvent = null
|
||||
}, DISPLAY_LENGTH)
|
||||
}
|
||||
|
||||
val aSet2 = AnimatorSet()
|
||||
private fun collectStartAnimations(): AnimatorSet {
|
||||
val animators = mutableListOf<Animator>()
|
||||
listeners.forEach { listener ->
|
||||
listener.onSystemEventAnimationBegin()?.let { anim ->
|
||||
animators.add(anim)
|
||||
}
|
||||
}
|
||||
animators.add(chipAnimationController.onSystemEventAnimationBegin())
|
||||
val animSet = AnimatorSet().also {
|
||||
it.playTogether(animators)
|
||||
}
|
||||
|
||||
aSet2.play(chipAnimator).before(systemAnimator)
|
||||
if (hasPersistentDot) {
|
||||
val dotAnim = notifyTransitionToPersistentDot()
|
||||
if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
|
||||
}
|
||||
return animSet
|
||||
}
|
||||
|
||||
aSet2.start()
|
||||
private fun collectFinishAnimations(): AnimatorSet {
|
||||
val animators = mutableListOf<Animator>()
|
||||
listeners.forEach { listener ->
|
||||
listener.onSystemEventAnimationFinish(hasPersistentDot)?.let { anim ->
|
||||
animators.add(anim)
|
||||
}
|
||||
}
|
||||
animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot))
|
||||
if (hasPersistentDot) {
|
||||
val dotAnim = notifyTransitionToPersistentDot()
|
||||
if (dotAnim != null) {
|
||||
animators.add(dotAnim)
|
||||
}
|
||||
}
|
||||
val animSet = AnimatorSet().also {
|
||||
it.playTogether(animators)
|
||||
}
|
||||
|
||||
scheduledEvent = null
|
||||
}, DISPLAY_LENGTH)
|
||||
}, DELAY)
|
||||
return animSet
|
||||
}
|
||||
|
||||
private fun notifyTransitionToPersistentDot(): Animator? {
|
||||
@@ -257,18 +281,6 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
return null
|
||||
}
|
||||
|
||||
private fun notifySystemStart() {
|
||||
listeners.forEach { it.onSystemChromeAnimationStart() }
|
||||
}
|
||||
|
||||
private fun notifySystemFinish() {
|
||||
listeners.forEach { it.onSystemChromeAnimationEnd() }
|
||||
}
|
||||
|
||||
private fun notifySystemAnimationUpdate(anim: ValueAnimator) {
|
||||
listeners.forEach { it.onSystemChromeAnimationUpdate(anim) }
|
||||
}
|
||||
|
||||
override fun addCallback(listener: SystemStatusAnimationCallback) {
|
||||
Assert.isMainThread()
|
||||
|
||||
@@ -287,24 +299,6 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val systemUpdateListener = ValueAnimator.AnimatorUpdateListener {
|
||||
anim -> notifySystemAnimationUpdate(anim)
|
||||
}
|
||||
|
||||
private val systemAnimatorAdapter = object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(p0: Animator?) {
|
||||
notifySystemFinish()
|
||||
}
|
||||
|
||||
override fun onAnimationStart(p0: Animator?) {
|
||||
notifySystemStart()
|
||||
}
|
||||
}
|
||||
|
||||
private val chipUpdateListener = ValueAnimator.AnimatorUpdateListener {
|
||||
anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
|
||||
}
|
||||
|
||||
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
|
||||
pw.println("Scheduled event: $scheduledEvent")
|
||||
pw.println("Has persistent privacy dot: $hasPersistentDot")
|
||||
@@ -318,24 +312,6 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class ChipAnimatorAdapter(
|
||||
@SystemAnimationState val endState: Int,
|
||||
val viewCreator: ViewCreator
|
||||
) : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(p0: Animator?) {
|
||||
chipAnimationController.onChipAnimationEnd(animationState)
|
||||
animationState = if (endState == SHOWING_PERSISTENT_DOT && !hasPersistentDot) {
|
||||
IDLE
|
||||
} else {
|
||||
endState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAnimationStart(p0: Animator?) {
|
||||
chipAnimationController.onChipAnimationStart(viewCreator, animationState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,16 +320,14 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
* create space for the chip animation to display. This means hiding the system elements in the
|
||||
* status bar and keyguard.
|
||||
*
|
||||
* TODO: the chip animation really only has one client, we can probably remove it from this
|
||||
* interface
|
||||
*
|
||||
* The value animators themselves are simple animators from 0.0 to 1.0. Listeners can apply any
|
||||
* interpolation they choose but realistically these are most likely to be simple alpha transitions
|
||||
*/
|
||||
interface SystemStatusAnimationCallback {
|
||||
@JvmDefault fun onSystemChromeAnimationUpdate(animator: ValueAnimator) {}
|
||||
@JvmDefault fun onSystemChromeAnimationStart() {}
|
||||
@JvmDefault fun onSystemChromeAnimationEnd() {}
|
||||
/** Implement this method to return an [Animator] or [AnimatorSet] that presents the chip */
|
||||
fun onSystemEventAnimationBegin(): Animator? { return null }
|
||||
/** Implement this method to return an [Animator] or [AnimatorSet] that hides the chip */
|
||||
fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null }
|
||||
|
||||
// Best method name, change my mind
|
||||
@JvmDefault
|
||||
@@ -363,50 +337,61 @@ interface SystemStatusAnimationCallback {
|
||||
@JvmDefault fun onHidePersistentDot(): Animator? { return null }
|
||||
}
|
||||
|
||||
interface SystemStatusChipAnimationCallback {
|
||||
fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {}
|
||||
|
||||
fun onChipAnimationStart(
|
||||
viewCreator: ViewCreator,
|
||||
@SystemAnimationState state: Int
|
||||
) {}
|
||||
|
||||
fun onChipAnimationEnd(@SystemAnimationState state: Int) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animation state IntDef
|
||||
*/
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@IntDef(
|
||||
value = [
|
||||
IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT, SHOWING_PERSISTENT_DOT
|
||||
IDLE,
|
||||
ANIMATION_QUEUED,
|
||||
ANIMATING_IN,
|
||||
RUNNING_CHIP_ANIM,
|
||||
ANIMATING_OUT,
|
||||
SHOWING_PERSISTENT_DOT
|
||||
]
|
||||
)
|
||||
annotation class SystemAnimationState
|
||||
|
||||
/** No animation is in progress */
|
||||
const val IDLE = 0
|
||||
/** An animation is queued, and awaiting the debounce period */
|
||||
const val ANIMATION_QUEUED = 1
|
||||
/** System is animating out, and chip is animating in */
|
||||
const val ANIMATING_IN = 1
|
||||
const val ANIMATING_IN = 2
|
||||
/** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */
|
||||
const val RUNNING_CHIP_ANIM = 2
|
||||
const val RUNNING_CHIP_ANIM = 3
|
||||
/** Chip is animating away and system is animating back */
|
||||
const val ANIMATING_OUT = 3
|
||||
const val ANIMATING_OUT = 4
|
||||
/** Chip has animated away, and the persistent dot is showing */
|
||||
const val SHOWING_PERSISTENT_DOT = 4
|
||||
const val SHOWING_PERSISTENT_DOT = 5
|
||||
|
||||
/** Commonly-needed interpolators can go here */
|
||||
@JvmField val STATUS_BAR_X_MOVE_OUT = PathInterpolator(0.33f, 0f, 0f, 1f)
|
||||
@JvmField val STATUS_BAR_X_MOVE_IN = PathInterpolator(0f, 0f, 0f, 1f)
|
||||
/**
|
||||
* Status chip animation to dot have multiple stages of motion, the _1 and _2 interpolators should
|
||||
* be used in succession
|
||||
*/
|
||||
val STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_1 = PathInterpolator(0.44f, 0f, 0.25f, 1f)
|
||||
val STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_2 = PathInterpolator(0.3f, 0f, 0.26f, 1f)
|
||||
val STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_1 = PathInterpolator(0.4f, 0f, 0.17f, 1f)
|
||||
val STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_2 = PathInterpolator(0.3f, 0f, 0f, 1f)
|
||||
val STATUS_CHIP_MOVE_TO_DOT = PathInterpolator(0f, 0f, 0.05f, 1f)
|
||||
|
||||
private const val TAG = "SystemStatusAnimationScheduler"
|
||||
private const val DELAY = 0L
|
||||
private const val DEBOUNCE_DELAY = 100L
|
||||
|
||||
/**
|
||||
* The total time spent animation should be 1500ms. The entrance animation is how much time
|
||||
* we give to the system to animate system elements out of the way. Total chip animation length
|
||||
* will be equivalent to 2*chip_anim_length + display_length
|
||||
* The total time spent on the chip animation is 1500ms, broken up into 3 sections:
|
||||
* - 500ms to animate the chip in (including animating system icons away)
|
||||
* - 500ms holding the chip on screen
|
||||
* - 500ms to animate the chip away (and system icons back)
|
||||
*
|
||||
* So DISPLAY_LENGTH should be the sum of the first 2 phases, while the final 500ms accounts for
|
||||
* the actual animation
|
||||
*/
|
||||
private const val ENTRANCE_ANIM_LENGTH = 250L
|
||||
private const val CHIP_ANIM_LENGTH = 250L
|
||||
// 1s + entrance time + chip anim_length
|
||||
private const val DISPLAY_LENGTH = 1500L
|
||||
private const val DISPLAY_LENGTH = 1000L
|
||||
|
||||
private const val MIN_UPTIME: Long = 5 * 1000
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
|
||||
}
|
||||
|
||||
private void updateKeyguardStatusBarHeight() {
|
||||
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
|
||||
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
|
||||
lp.height = getStatusBarHeaderHeightKeyguard(mContext);
|
||||
setLayoutParams(lp);
|
||||
}
|
||||
@@ -510,28 +510,6 @@ public class KeyguardStatusBarView extends RelativeLayout {
|
||||
}
|
||||
}
|
||||
|
||||
void onSystemChromeAnimationStart(boolean isAnimatingOut) {
|
||||
if (isAnimatingOut) {
|
||||
mSystemIconsContainer.setVisibility(View.VISIBLE);
|
||||
mSystemIconsContainer.setAlpha(0f);
|
||||
}
|
||||
}
|
||||
|
||||
void onSystemChromeAnimationEnd(boolean isAnimatingIn) {
|
||||
// Make sure the system icons are out of the way
|
||||
if (isAnimatingIn) {
|
||||
mSystemIconsContainer.setVisibility(View.INVISIBLE);
|
||||
mSystemIconsContainer.setAlpha(0f);
|
||||
} else {
|
||||
mSystemIconsContainer.setAlpha(1f);
|
||||
mSystemIconsContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
void onSystemChromeAnimationUpdate(float animatedValue) {
|
||||
mSystemIconsContainer.setAlpha(animatedValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
|
||||
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
|
||||
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -47,6 +45,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty;
|
||||
import com.android.systemui.statusbar.notification.PropertyAnimator;
|
||||
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
|
||||
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
|
||||
import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
|
||||
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
|
||||
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
|
||||
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
|
||||
@@ -107,6 +106,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
|
||||
@Override
|
||||
public void onDensityOrFontScaleChanged() {
|
||||
mView.loadDimens();
|
||||
// The animator is dependent on resources for offsets
|
||||
mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,21 +124,16 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
|
||||
|
||||
private final SystemStatusAnimationCallback mAnimationCallback =
|
||||
new SystemStatusAnimationCallback() {
|
||||
@NonNull
|
||||
@Override
|
||||
public void onSystemChromeAnimationStart() {
|
||||
mView.onSystemChromeAnimationStart(
|
||||
mAnimationScheduler.getAnimationState() == ANIMATING_OUT);
|
||||
public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) {
|
||||
return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public void onSystemChromeAnimationEnd() {
|
||||
mView.onSystemChromeAnimationEnd(
|
||||
mAnimationScheduler.getAnimationState() == ANIMATING_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator anim) {
|
||||
mView.onSystemChromeAnimationUpdate((float) anim.getAnimatedValue());
|
||||
public Animator onSystemEventAnimationBegin() {
|
||||
return mSystemEventAnimator.onSystemEventAnimationBegin();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -232,6 +228,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
|
||||
private int mStatusBarState;
|
||||
private boolean mDozing;
|
||||
private boolean mShowingKeyguardHeadsUp;
|
||||
private StatusBarSystemEventAnimator mSystemEventAnimator;
|
||||
|
||||
@Inject
|
||||
public KeyguardStatusBarViewController(
|
||||
@@ -302,6 +299,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
|
||||
mView.setKeyguardUserAvatarEnabled(
|
||||
!mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
|
||||
mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
|
||||
mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,12 +20,10 @@ import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
|
||||
import static android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP;
|
||||
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
|
||||
|
||||
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
|
||||
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
|
||||
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
|
||||
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.Animator;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -136,6 +134,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
}
|
||||
};
|
||||
private OperatorNameViewController mOperatorNameViewController;
|
||||
private StatusBarSystemEventAnimator mSystemEventAnimator;
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
public CollapsedStatusBarFragment(
|
||||
@@ -210,7 +209,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
initEmergencyCryptkeeperText();
|
||||
initOperatorName();
|
||||
initNotificationIconArea();
|
||||
mAnimationScheduler.addCallback(this);
|
||||
mSystemEventAnimator =
|
||||
new StatusBarSystemEventAnimator(mSystemIconArea, getResources());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -245,6 +245,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
mCommandQueue.addCallback(this);
|
||||
mStatusBarStateController.addCallback(this);
|
||||
initOngoingCallChip();
|
||||
mAnimationScheduler.addCallback(this);
|
||||
|
||||
mSecureSettings.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON),
|
||||
@@ -258,6 +259,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
mCommandQueue.removeCallback(this);
|
||||
mStatusBarStateController.removeCallback(this);
|
||||
mOngoingCallController.removeCallback(mOngoingCallListener);
|
||||
mAnimationScheduler.removeCallback(this);
|
||||
mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
|
||||
}
|
||||
|
||||
@@ -265,7 +267,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mStatusBarIconController.removeIconGroup(mDarkIconManager);
|
||||
mAnimationScheduler.removeCallback(this);
|
||||
if (mNetworkController.hasEmergencyCryptKeeperText()) {
|
||||
mNetworkController.removeCallback(mSignalCallback);
|
||||
}
|
||||
@@ -576,35 +577,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
||||
disable(getContext().getDisplayId(), mDisabled1, mDisabled2, false /* animate */);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void onSystemChromeAnimationStart() {
|
||||
if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT
|
||||
&& !isSystemIconAreaDisabled()) {
|
||||
mSystemIconArea.setVisibility(View.VISIBLE);
|
||||
mSystemIconArea.setAlpha(0f);
|
||||
}
|
||||
public Animator onSystemEventAnimationBegin() {
|
||||
return mSystemEventAnimator.onSystemEventAnimationBegin();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void onSystemChromeAnimationEnd() {
|
||||
// Make sure the system icons are out of the way
|
||||
if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
|
||||
mSystemIconArea.setVisibility(View.INVISIBLE);
|
||||
mSystemIconArea.setAlpha(0f);
|
||||
} else {
|
||||
if (isSystemIconAreaDisabled()) {
|
||||
// don't unhide
|
||||
return;
|
||||
}
|
||||
|
||||
mSystemIconArea.setAlpha(1f);
|
||||
mSystemIconArea.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator animator) {
|
||||
mSystemIconArea.setAlpha((float) animator.getAnimatedValue());
|
||||
public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) {
|
||||
return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot);
|
||||
}
|
||||
|
||||
private boolean isSystemIconAreaDisabled() {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.statusbar.phone.fragment
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.res.Resources
|
||||
import android.view.View
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_IN
|
||||
import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT
|
||||
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback
|
||||
|
||||
/**
|
||||
* Tied directly to [SystemStatusAnimationScheduler]. Any StatusBar-like thing (keyguard, collapsed
|
||||
* status bar fragment), can just feed this an animatable view to get the default system status
|
||||
* animation.
|
||||
*
|
||||
* This animator relies on resources, and should be recreated whenever resources are updated. While
|
||||
* this class could be used directly as the animation callback, it's probably best to forward calls
|
||||
* to it so that it can be recreated at any moment without needing to remove/add callback.
|
||||
*/
|
||||
class StatusBarSystemEventAnimator(
|
||||
val animatedView: View,
|
||||
resources: Resources
|
||||
) : SystemStatusAnimationCallback {
|
||||
private val translationXIn: Int = resources.getDimensionPixelSize(
|
||||
R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
|
||||
private val translationXOut: Int = resources.getDimensionPixelSize(
|
||||
R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
|
||||
|
||||
override fun onSystemEventAnimationBegin(): Animator {
|
||||
val moveOut = ValueAnimator.ofFloat(0f, 1f).setDuration(383)
|
||||
moveOut.interpolator = STATUS_BAR_X_MOVE_OUT
|
||||
moveOut.addUpdateListener { animation: ValueAnimator ->
|
||||
animatedView.translationX = -(translationXIn * animation.animatedValue as Float)
|
||||
}
|
||||
val alphaOut = ValueAnimator.ofFloat(1f, 0f).setDuration(133)
|
||||
alphaOut.interpolator = null
|
||||
alphaOut.addUpdateListener { animation: ValueAnimator ->
|
||||
animatedView.alpha = animation.animatedValue as Float
|
||||
}
|
||||
|
||||
val animSet = AnimatorSet()
|
||||
animSet.playTogether(moveOut, alphaOut)
|
||||
return animSet
|
||||
}
|
||||
|
||||
override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
|
||||
animatedView.translationX = translationXOut.toFloat()
|
||||
val moveIn = ValueAnimator.ofFloat(1f, 0f).setDuration(467)
|
||||
moveIn.startDelay = 33
|
||||
moveIn.interpolator = STATUS_BAR_X_MOVE_IN
|
||||
moveIn.addUpdateListener { animation: ValueAnimator ->
|
||||
animatedView.translationX = translationXOut * animation.animatedValue as Float
|
||||
}
|
||||
val alphaIn = ValueAnimator.ofFloat(0f, 1f).setDuration(167)
|
||||
alphaIn.startDelay = 67
|
||||
alphaIn.interpolator = null
|
||||
alphaIn.addUpdateListener { animation: ValueAnimator ->
|
||||
animatedView.alpha = animation.animatedValue as Float
|
||||
}
|
||||
|
||||
val animatorSet = AnimatorSet()
|
||||
animatorSet.playTogether(moveIn, alphaIn)
|
||||
|
||||
return animatorSet
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user