System status event animation polish (1/n)
First part of polishing the system status event animations. - Define a new type `BackgroundAnimatableView` that allows the chip animation controller to animate the bounds of the chip - Define a `ViewCreator` type to simplify some args - Rework the animation to do simple alpha transition and animate the width of the chip rather than translationX - Variuos other quality of life fixes Test: manual Bug: 187881849 Change-Id: I22b7defb8c055a05ac26d0d9e4aa688e120220aa
This commit is contained in:
@@ -22,7 +22,10 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:focusable="true" >
|
||||
android:focusable="true"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icons_container"
|
||||
@@ -34,5 +37,7 @@
|
||||
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>
|
||||
@@ -19,17 +19,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
android:paddingEnd="8dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dot_view"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:src="@drawable/system_animation_ongoing_dot"
|
||||
android:visibility="invisible"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -22,13 +22,14 @@ import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.events.BackgroundAnimatableView
|
||||
|
||||
class OngoingPrivacyChip @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttrs: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
|
||||
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView {
|
||||
|
||||
private var iconMargin = 0
|
||||
private var iconSize = 0
|
||||
@@ -50,6 +51,16 @@ class OngoingPrivacyChip @JvmOverloads constructor(
|
||||
updateResources()
|
||||
}
|
||||
|
||||
/**
|
||||
* When animating as a chip in the status bar, we want to animate the width for the container
|
||||
* of the privacy items. We have to subtract our own top and left offset because the bounds
|
||||
* come to us as absolute on-screen bounds, and `iconsContainer` is laid out relative to the
|
||||
* frame layout's bounds.
|
||||
*/
|
||||
override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
|
||||
iconsContainer.setLeftTopRightBottom(l - left, t - top, r - left, b - top)
|
||||
}
|
||||
|
||||
// Should only be called if the builder icons or app changed
|
||||
private fun updateView(builder: PrivacyChipBuilder) {
|
||||
fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar.events
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
@@ -27,13 +28,15 @@ import com.android.systemui.R
|
||||
import com.android.systemui.privacy.OngoingPrivacyChip
|
||||
import com.android.systemui.privacy.PrivacyItem
|
||||
|
||||
typealias ViewCreator = (context: Context) -> BackgroundAnimatableView
|
||||
|
||||
interface StatusEvent {
|
||||
val priority: Int
|
||||
// Whether or not to force the status bar open and show a dot
|
||||
val forceVisible: Boolean
|
||||
// Whether or not to show an animation for this event
|
||||
val showAnimation: Boolean
|
||||
val viewCreator: (context: Context) -> View
|
||||
val viewCreator: ViewCreator
|
||||
var contentDescription: String?
|
||||
|
||||
// Update this event with values from another event.
|
||||
@@ -47,14 +50,37 @@ interface StatusEvent {
|
||||
}
|
||||
}
|
||||
|
||||
class BGView(
|
||||
context: Context
|
||||
) : View(context), BackgroundAnimatableView {
|
||||
override val view: View
|
||||
get() = this
|
||||
|
||||
override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
|
||||
setLeftTopRightBottom(l, t, r, b)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("AppCompatCustomView")
|
||||
class BGImageView(
|
||||
context: Context
|
||||
) : ImageView(context), BackgroundAnimatableView {
|
||||
override val view: View
|
||||
get() = this
|
||||
|
||||
override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) {
|
||||
setLeftTopRightBottom(l, t, r, b)
|
||||
}
|
||||
}
|
||||
|
||||
class BatteryEvent : StatusEvent {
|
||||
override val priority = 50
|
||||
override val forceVisible = false
|
||||
override val showAnimation = true
|
||||
override var contentDescription: String? = ""
|
||||
|
||||
override val viewCreator: (context: Context) -> View = { context ->
|
||||
val iv = ImageView(context)
|
||||
override val viewCreator: (context: Context) -> BGImageView = { context ->
|
||||
val iv = BGImageView(context)
|
||||
iv.setImageDrawable(ThemedBatteryDrawable(context, Color.WHITE))
|
||||
iv.setBackgroundDrawable(ColorDrawable(Color.GREEN))
|
||||
iv
|
||||
@@ -72,7 +98,7 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
|
||||
var privacyItems: List<PrivacyItem> = listOf()
|
||||
private var privacyChip: OngoingPrivacyChip? = null
|
||||
|
||||
override val viewCreator: (context: Context) -> View = { context ->
|
||||
override val viewCreator: ViewCreator = { context ->
|
||||
val v = LayoutInflater.from(context)
|
||||
.inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip
|
||||
v.privacyList = privacyItems
|
||||
@@ -82,7 +108,7 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return javaClass.simpleName
|
||||
return "${javaClass.simpleName}(forceVisible=$forceVisible, privacyItems=$privacyItems)"
|
||||
}
|
||||
|
||||
override fun shouldUpdateFromEvent(other: StatusEvent?): Boolean {
|
||||
|
||||
@@ -18,14 +18,16 @@ package com.android.systemui.statusbar.events
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Point
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec.AT_MOST
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.FrameLayout
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
|
||||
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowController
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -35,44 +37,73 @@ import javax.inject.Inject
|
||||
class SystemEventChipAnimationController @Inject constructor(
|
||||
private val context: Context,
|
||||
private val statusBarWindowController: StatusBarWindowController,
|
||||
private val locationPublisher: StatusBarLocationPublisher
|
||||
private val contentInsetsProvider: StatusBarContentInsetsProvider
|
||||
) : SystemStatusChipAnimationCallback {
|
||||
var showPersistentDot = false
|
||||
set(value) {
|
||||
field = value
|
||||
statusBarWindowController.setForceStatusBarVisible(value)
|
||||
maybeUpdateShowDot()
|
||||
}
|
||||
|
||||
private lateinit var animationWindowView: FrameLayout
|
||||
private lateinit var animationDotView: View
|
||||
private var currentAnimatedView: View? = null
|
||||
|
||||
private var currentAnimatedView: BackgroundAnimatableView? = null
|
||||
|
||||
// Left for LTR, Right for RTL
|
||||
private var animationDirection = LEFT
|
||||
private var chipRight = 0
|
||||
private var chipLeft = 0
|
||||
private var chipWidth = 0
|
||||
private var dotCenter = Point(0, 0)
|
||||
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
|
||||
|
||||
// TODO: move to dagger
|
||||
private var initialized = false
|
||||
|
||||
override fun onChipAnimationStart(
|
||||
viewCreator: (context: Context) -> View,
|
||||
viewCreator: ViewCreator,
|
||||
@SystemAnimationState state: Int
|
||||
) {
|
||||
if (!initialized) init()
|
||||
|
||||
if (state == ANIMATING_IN) {
|
||||
currentAnimatedView = viewCreator(context)
|
||||
animationWindowView.addView(currentAnimatedView, layoutParamsDefault())
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// We are animating IN; chip comes in from View.END
|
||||
currentAnimatedView?.apply {
|
||||
val translation = width.toFloat()
|
||||
translationX = if (isLayoutRtl) -translation else translation
|
||||
alpha = 0f
|
||||
visibility = View.VISIBLE
|
||||
setPadding(locationPublisher.marginLeft, 0, locationPublisher.marginRight, 0)
|
||||
updateAnimatedViewBoundsForAmount(0.1f, this)
|
||||
}
|
||||
} else {
|
||||
// We are animating away
|
||||
currentAnimatedView?.apply {
|
||||
translationX = 0f
|
||||
currentAnimatedView!!.view.apply {
|
||||
alpha = 1f
|
||||
}
|
||||
}
|
||||
@@ -82,15 +113,14 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
if (state == ANIMATING_IN) {
|
||||
// Finished animating in
|
||||
currentAnimatedView?.apply {
|
||||
translationX = 0f
|
||||
alpha = 1f
|
||||
updateAnimatedViewBoundsForAmount(1f, this)
|
||||
}
|
||||
} else {
|
||||
// Finished animating away
|
||||
currentAnimatedView?.apply {
|
||||
currentAnimatedView!!.view.apply {
|
||||
visibility = View.INVISIBLE
|
||||
}
|
||||
animationWindowView.removeView(currentAnimatedView)
|
||||
animationWindowView.removeView(currentAnimatedView!!.view)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,22 +128,10 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
animator: ValueAnimator,
|
||||
@SystemAnimationState state: Int
|
||||
) {
|
||||
// Alpha is parameterized 0,1, and translation from (width, 0)
|
||||
currentAnimatedView?.apply {
|
||||
val amt = animator.animatedValue as Float
|
||||
|
||||
alpha = amt
|
||||
|
||||
val w = width
|
||||
val translation = (1 - amt) * w
|
||||
translationX = if (isLayoutRtl) -translation else translation
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeUpdateShowDot() {
|
||||
if (!initialized) return
|
||||
if (!showPersistentDot && currentAnimatedView == null) {
|
||||
animationDotView.visibility = View.INVISIBLE
|
||||
val amt = (animator.animatedValue as Float).amt()
|
||||
view.alpha = (animator.animatedValue as Float)
|
||||
updateAnimatedViewBoundsForAmount(amt, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,19 +139,56 @@ class SystemEventChipAnimationController @Inject constructor(
|
||||
initialized = true
|
||||
animationWindowView = LayoutInflater.from(context)
|
||||
.inflate(R.layout.system_event_animation_window, null) as FrameLayout
|
||||
animationDotView = animationWindowView.findViewById(R.id.dot_view)
|
||||
val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||
lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||
statusBarWindowController.addViewToWindow(animationWindowView, lp)
|
||||
animationWindowView.clipToPadding = false
|
||||
animationWindowView.clipChildren = false
|
||||
animationWindowView.measureAllChildren = true
|
||||
}
|
||||
|
||||
private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams =
|
||||
FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
|
||||
it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||
it.marginEnd = marginEnd
|
||||
}
|
||||
|
||||
private fun updateAnimatedViewBoundsForAmount(amt: Float, chip: BackgroundAnimatableView) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
|
||||
private fun right() = locationPublisher.marginRight
|
||||
private fun left() = locationPublisher.marginLeft
|
||||
|
||||
private fun layoutParamsDefault(): FrameLayout.LayoutParams =
|
||||
FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
|
||||
it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||
it.marginStart = start()
|
||||
}
|
||||
private fun right() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().right
|
||||
private fun left() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().left
|
||||
private fun Float.amt() = 0.01f.coerceAtLeast(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Chips should provide a view that can be animated with something better than a fade-in
|
||||
*/
|
||||
interface BackgroundAnimatableView {
|
||||
val view: View // Since this can't extend View, add a view prop
|
||||
get() = this as View
|
||||
val chipWidth: Int
|
||||
get() = view.measuredWidth
|
||||
fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int)
|
||||
}
|
||||
|
||||
// Animation directions
|
||||
private const val LEFT = 1
|
||||
private const val RIGHT = 2
|
||||
|
||||
@@ -70,11 +70,11 @@ class SystemEventCoordinator @Inject constructor(
|
||||
fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) {
|
||||
val event = PrivacyEvent(showAnimation)
|
||||
event.privacyItems = privacyStateListener.currentPrivacyItems
|
||||
event.contentDescription = {
|
||||
event.contentDescription = run {
|
||||
val items = PrivacyChipBuilder(context, event.privacyItems).joinTypes()
|
||||
context.getString(
|
||||
R.string.ongoing_privacy_chip_content_multiple_apps, items)
|
||||
}()
|
||||
}
|
||||
scheduler.onStatusEvent(event)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.IntDef
|
||||
import android.content.Context
|
||||
import android.os.Process
|
||||
import android.provider.DeviceConfig
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
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
|
||||
@@ -45,7 +45,7 @@ import javax.inject.Inject
|
||||
* Dead-simple scheduler for system status events. Obeys the following principles (all values TBD):
|
||||
* - Avoiding log spam by only allowing 12 events per minute (1event/5s)
|
||||
* - Waits 100ms to schedule any event for debouncing/prioritization
|
||||
* - Simple prioritization: Privacy > Battery > connectivity (encoded in StatusEvent)
|
||||
* - Simple prioritization: Privacy > Battery > connectivity (encoded in [StatusEvent])
|
||||
* - Only schedules a single event, and throws away lowest priority events
|
||||
*
|
||||
* There are 4 basic stages of animation at play here:
|
||||
@@ -111,7 +111,7 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
scheduleEvent(event)
|
||||
} else if (scheduledEvent?.shouldUpdateFromEvent(event) == true) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updating current event from: $event")
|
||||
Log.d(TAG, "updating current event from: $event. animationState=$animationState")
|
||||
}
|
||||
scheduledEvent?.updateFromEvent(event)
|
||||
if (event.forceVisible) {
|
||||
@@ -172,12 +172,14 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
entranceAnimator.duration = ENTRANCE_ANIM_LENGTH
|
||||
entranceAnimator.addListener(systemAnimatorAdapter)
|
||||
entranceAnimator.addUpdateListener(systemUpdateListener)
|
||||
entranceAnimator.interpolator = STANDARD_ACCELERATE
|
||||
|
||||
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 aSet2 = AnimatorSet()
|
||||
aSet2.playSequentially(entranceAnimator, chipAnimator)
|
||||
@@ -190,6 +192,12 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
val chipAnimator = ValueAnimator.ofFloat(1f, 0f)
|
||||
chipAnimator.duration = CHIP_ANIM_LENGTH
|
||||
@@ -201,6 +209,7 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
chipAnimator.addListener(
|
||||
ChipAnimatorAdapter(endState, scheduledEvent!!.viewCreator))
|
||||
chipAnimator.addUpdateListener(chipUpdateListener)
|
||||
chipAnimator.interpolator = STANDARD_ACCELERATE
|
||||
|
||||
val aSet2 = AnimatorSet()
|
||||
|
||||
@@ -212,7 +221,6 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
|
||||
aSet2.start()
|
||||
|
||||
statusBarWindowController.setForceStatusBarVisible(false)
|
||||
scheduledEvent = null
|
||||
}, DISPLAY_LENGTH)
|
||||
}, DELAY)
|
||||
@@ -313,7 +321,7 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
|
||||
inner class ChipAnimatorAdapter(
|
||||
@SystemAnimationState val endState: Int,
|
||||
val viewCreator: (context: Context) -> View
|
||||
val viewCreator: ViewCreator
|
||||
) : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(p0: Animator?) {
|
||||
chipAnimationController.onChipAnimationEnd(animationState)
|
||||
@@ -359,7 +367,7 @@ interface SystemStatusChipAnimationCallback {
|
||||
fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {}
|
||||
|
||||
fun onChipAnimationStart(
|
||||
viewCreator: (context: Context) -> View,
|
||||
viewCreator: ViewCreator,
|
||||
@SystemAnimationState state: Int
|
||||
) {}
|
||||
|
||||
@@ -371,7 +379,7 @@ interface SystemStatusChipAnimationCallback {
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@IntDef(
|
||||
value = [
|
||||
IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT
|
||||
IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT, SHOWING_PERSISTENT_DOT
|
||||
]
|
||||
)
|
||||
annotation class SystemAnimationState
|
||||
|
||||
Reference in New Issue
Block a user