summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2025-02-18 10:07:38 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-18 10:07:38 -0800
commite9fca3411bf577b1e37cccdfe2f9e84acd603717 (patch)
tree79e1617b0583b1608f3a54f54a601414adcfad39
parented3076898d3bd18beeaa4f6f25ae8d51f7b63a1d (diff)
parent433cab73fd84fc768eee48c9fd17df8564bb5260 (diff)
Merge "Use physics for notification movement" into main
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt220
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java183
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java5
21 files changed, 775 insertions, 171 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 29b578ae6e48..f981545008fa 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1915,6 +1915,13 @@ flag {
}
flag {
+ name: "physical_notification_movement"
+ namespace: "systemui"
+ description: "Make notifications use physics based animations for movement"
+ bug: "393581344"
+}
+
+flag {
name: "glanceable_hub_direct_edit_mode"
namespace: "systemui"
description: "Invokes edit mode directly from long press in glanceable hub"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
index 05d9495db091..a8aac39540fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -74,7 +74,7 @@ class DragDownHelperTest : SysuiTestCase() {
dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0)
- verify(expandableView, atLeast(1)).actualHeight = collapsedHeight
+ verify(expandableView, atLeast(1)).setFinalActualHeight(collapsedHeight)
}
@Test
@@ -83,6 +83,6 @@ class DragDownHelperTest : SysuiTestCase() {
dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0)
- verify(expandableView, never()).actualHeight = anyInt()
+ verify(expandableView, never()).setFinalActualHeight(anyInt())
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
index cd66ef32180a..242da0bacea3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -86,7 +86,7 @@ class PulseExpansionHandlerTest : SysuiTestCase() {
pulseExpansionHandler.reset(expandableView, animationDuration = 0)
- verify(expandableView, atLeast(1)).actualHeight = collapsedHeight
+ verify(expandableView, atLeast(1)).setFinalActualHeight(collapsedHeight)
}
@Test
@@ -95,6 +95,6 @@ class PulseExpansionHandlerTest : SysuiTestCase() {
pulseExpansionHandler.reset(expandableView, animationDuration = 0)
- verify(expandableView, never()).actualHeight = anyInt()
+ verify(expandableView, never()).setFinalActualHeight(anyInt())
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
new file mode 100644
index 000000000000..56cd72e7725f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2017 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.notification
+
+import android.util.FloatProperty
+import android.util.Property
+import android.view.View
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.stack.AnimationProperties
+import com.android.systemui.statusbar.notification.stack.ViewState
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@UiThreadTest
+class PhysicsPropertyAnimatorTest : SysuiTestCase() {
+ private var view: View = View(context)
+ private val effectiveProperty =
+ object : FloatProperty<View>("TEST") {
+ private var _value: Float = 100f
+
+ override fun setValue(view: View, value: Float) {
+ this._value = value
+ }
+
+ override fun get(`object`: View): Float {
+ return _value
+ }
+ }
+ private val property: PhysicsProperty =
+ PhysicsProperty(R.id.scale_x_animator_tag, effectiveProperty)
+ private var finishListener: DynamicAnimation.OnAnimationEndListener? = null
+ private val animationProperties: AnimationProperties = AnimationProperties()
+
+ @Before
+ fun setUp() {
+ finishListener = Mockito.mock(DynamicAnimation.OnAnimationEndListener::class.java)
+ }
+
+ @Test
+ fun testAnimationStarted() {
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true, /* animate */
+ )
+ Assert.assertTrue(PhysicsPropertyAnimator.isAnimating(view, property))
+ }
+
+ @Test
+ fun testNoAnimationStarted() {
+ PhysicsPropertyAnimator.setProperty(view, property, 200f, animationProperties, false)
+ Assert.assertFalse(PhysicsPropertyAnimator.isAnimating(view, property))
+ }
+
+ @Test
+ fun testEndValueUpdated() {
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true, /* animate */
+ )
+ Assert.assertEquals(
+ (ViewState.getChildTag(view, property.tag) as PropertyData).finalValue,
+ 200f,
+ )
+ }
+
+ @Test
+ fun testOffset() {
+ effectiveProperty.setValue(view, 100f)
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true, /* animate */
+ )
+ val propertyData = ViewState.getChildTag(view, property.tag) as PropertyData
+ Assert.assertEquals(propertyData.finalValue, 200f)
+ Assert.assertEquals(propertyData.offset, -100f)
+ }
+
+ @Test
+ fun testValueIsSetUnAnimated() {
+ effectiveProperty.setValue(view, 100f)
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ false, /* animate */
+ )
+ Assert.assertEquals(200f, effectiveProperty[view])
+ }
+
+ @Test
+ fun testAnimationToRightValueUpdated() {
+ effectiveProperty.setValue(view, 100f)
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true, /* animate */
+ )
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 220f,
+ animationProperties,
+ false, /* animate */
+ )
+ Assert.assertTrue(PhysicsPropertyAnimator.isAnimating(view, property))
+ Assert.assertEquals(120f, effectiveProperty[view])
+ Assert.assertEquals(
+ (ViewState.getChildTag(view, property.tag) as PropertyData).finalValue,
+ 220f,
+ )
+ }
+
+ @Test
+ fun testAnimationToRightValueUpdateAnimated() {
+ effectiveProperty.setValue(view, 100f)
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true, /* animate */
+ )
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 220f,
+ animationProperties,
+ true, /* animate */
+ )
+ Assert.assertTrue(PhysicsPropertyAnimator.isAnimating(view, property))
+ Assert.assertEquals(100f, effectiveProperty[view])
+ val propertyData = ViewState.getChildTag(view, property.tag) as PropertyData
+ Assert.assertEquals(propertyData.finalValue, 220f)
+ Assert.assertEquals(propertyData.offset, -120f)
+ }
+
+ @Test
+ fun testUsingDelay() {
+ effectiveProperty.setValue(view, 100f)
+ animationProperties.setDelay(200)
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true, /* animate */
+ )
+ val propertyData = ViewState.getChildTag(view, property.tag) as PropertyData
+ Assert.assertNotNull(propertyData.delayRunnable)
+ Assert.assertFalse(propertyData.animator?.isRunning ?: true)
+ }
+
+ @Test
+ fun testUsingListener() {
+ PhysicsPropertyAnimator.setProperty(
+ view,
+ property,
+ 200f,
+ animationProperties,
+ true,
+ finishListener,
+ )
+ val propertyData = ViewState.getChildTag(view, property.tag) as PropertyData
+ propertyData.animator?.cancel()
+ Mockito.verify(finishListener!!).onAnimationEnd(any(), any(), any(), any())
+ }
+
+ @Test
+ fun testUsingListenerProperties() {
+ val finishListener2 = Mockito.mock(DynamicAnimation.OnAnimationEndListener::class.java)
+ val animationProperties: AnimationProperties =
+ object : AnimationProperties() {
+ override fun getAnimationEndListener(
+ property: Property<*, *>?
+ ): DynamicAnimation.OnAnimationEndListener {
+ return finishListener2
+ }
+ }
+ PhysicsPropertyAnimator.setProperty(view, property, 200f, animationProperties, true)
+ val propertyData = ViewState.getChildTag(view, property.tag) as PropertyData
+ propertyData.animator?.cancel()
+ Mockito.verify(finishListener2).onAnimationEnd(any(), any(), any(), any())
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
index 3a77d822eb7e..52f903e20ab8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
@@ -31,7 +31,7 @@ class MediaContainerViewTest : SysuiTestCase() {
fun testUpdateClipping_updatesClipHeight() {
assertTrue(mediaContainerView.clipHeight == 0)
- mediaContainerView.actualHeight = 10
+ mediaContainerView.setFinalActualHeight(10)
mediaContainerView.updateClipping()
assertTrue(mediaContainerView.clipHeight == 10)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index e4dd29ad83b0..67415de86d9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -81,7 +81,7 @@ class StackStateAnimatorTest : SysuiTestCase() {
stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
- verify(view).setActualHeight(VIEW_HEIGHT, false)
+ verify(view).setFinalActualHeight(VIEW_HEIGHT)
verify(view, description("should animate from the top")).translationY = expectedStartY
verify(view)
.performAddAnimation(
@@ -104,7 +104,7 @@ class StackStateAnimatorTest : SysuiTestCase() {
stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
- verify(view).setActualHeight(VIEW_HEIGHT, false)
+ verify(view).setFinalActualHeight(VIEW_HEIGHT)
verify(view, description("should animate from the bottom")).translationY = expectedStartY
verify(view)
.performAddAnimation(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
index e493420b64a1..ef415c918f91 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
@@ -16,21 +16,28 @@
package com.android.systemui.statusbar.notification.stack
+import android.animation.ValueAnimator
+import android.view.View
+import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.assertDoesNotLogWtf
import com.android.systemui.log.assertLogsWtf
-import kotlin.math.log2
-import kotlin.math.sqrt
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.TAG_ANIMATOR_TRANSLATION_Y
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.Y_TRANSLATION
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
+import kotlin.math.log2
+import kotlin.math.sqrt
@RunWith(AndroidJUnit4::class)
@SmallTest
+@UiThreadTest
class ViewStateTest : SysuiTestCase() {
- private val viewState = ViewState()
+ private val viewState = ViewState(true /* usePhysicsForMovement */)
@Suppress("DIVISION_BY_ZERO")
@Test
@@ -64,4 +71,37 @@ class ViewStateTest : SysuiTestCase() {
assertLogsWtf { viewState.scaleY = Float.POSITIVE_INFINITY * 0 }
Assert.assertEquals(viewState.scaleY, 0.25f)
}
+
+ @Test
+ fun testUsingPhysics() {
+ val animatedView = View(context)
+ viewState.setUsePhysicsForMovement(true)
+ viewState.applyToView(animatedView)
+ viewState.yTranslation = 100f
+ val animationFilter = AnimationFilter().animateY()
+ val animationProperties = object : AnimationProperties() {
+ override fun getAnimationFilter(): AnimationFilter {
+ return animationFilter
+ }
+ }
+ viewState.animateTo(animatedView, animationProperties)
+ Assert.assertTrue(PhysicsPropertyAnimator.isAnimating(animatedView, Y_TRANSLATION))
+ }
+
+ @Test
+ fun testNotUsingPhysics() {
+ val animatedView = View(context)
+ viewState.setUsePhysicsForMovement(false)
+ viewState.applyToView(animatedView)
+ viewState.yTranslation = 100f
+ val animationFilter = AnimationFilter().animateY()
+ val animationProperties = object : AnimationProperties() {
+ override fun getAnimationFilter(): AnimationFilter {
+ return animationFilter
+ }
+ }
+ viewState.animateTo(animatedView, animationProperties)
+ val tag = animatedView.getTag(TAG_ANIMATOR_TRANSLATION_Y)
+ Assert.assertTrue(tag is ValueAnimator)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 42896a419658..b2cb357fabc8 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -167,7 +167,7 @@ public class ExpandHelper implements Gefingerpoken {
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
- mView.setActualHeight((int) h);
+ mView.setFinalActualHeight((int) h);
mCurrentHeight = h;
}
public float getHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 10f61c66c838..5b5058fbc6c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -798,6 +798,7 @@ class DragDownHelper(
initialTouchY = y
initialTouchX = x
}
+
MotionEvent.ACTION_MOVE -> {
val h = y - initialTouchY
// Adjust the touch slop if another gesture may be being performed.
@@ -852,6 +853,7 @@ class DragDownHelper(
}
return true
}
+
MotionEvent.ACTION_UP ->
if (
!falsingManager.isUnlockingDisabled &&
@@ -871,6 +873,7 @@ class DragDownHelper(
stopDragging()
return false
}
+
MotionEvent.ACTION_CANCEL -> {
stopDragging()
return false
@@ -910,7 +913,7 @@ class DragDownHelper(
overshoot *= 1 - RUBBERBAND_FACTOR_STATIC
rubberband -= overshoot
}
- child.actualHeight = (child.collapsedHeight + rubberband).toInt()
+ child.setFinalActualHeight((child.collapsedHeight + rubberband).toInt())
}
@VisibleForTesting
@@ -927,7 +930,7 @@ class DragDownHelper(
anim.duration = animationDuration
anim.addUpdateListener { animation: ValueAnimator ->
// don't use reflection, because the `actualHeight` field may be obfuscated
- child.actualHeight = animation.animatedValue as Int
+ child.setFinalActualHeight(animation.animatedValue as Int)
}
anim.addListener(
object : AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 85b8bf9aec80..3be7682fe250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -140,8 +140,8 @@ constructor(
private fun canHandleMotionEvent(): Boolean {
return wakeUpCoordinator.canShowPulsingHuns &&
- !shadeInteractor.isQsExpanded.value &&
- !bouncerShowing
+ !shadeInteractor.isQsExpanded.value &&
+ !bouncerShowing
}
private fun startExpansion(event: MotionEvent): Boolean {
@@ -194,7 +194,7 @@ constructor(
override fun onTouchEvent(event: MotionEvent): Boolean {
val finishExpanding =
(event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_UP) &&
- isExpanding
+ isExpanding
val isDraggingNotificationOrCanBypass =
mStartingChild?.showingPulsing() == true || bypassController.canBypass()
@@ -218,8 +218,8 @@ constructor(
velocityTracker!!.computeCurrentVelocity(/* units= */ 1000)
val canExpand =
moveDistance > 0 &&
- velocityTracker!!.getYVelocity() > -1000 &&
- statusBarStateController.state != StatusBarState.SHADE
+ velocityTracker!!.getYVelocity() > -1000 &&
+ statusBarStateController.state != StatusBarState.SHADE
if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
@@ -266,11 +266,11 @@ constructor(
val child = mStartingChild!!
val newHeight =
Math.min((child.collapsedHeight + expansionHeight).toInt(), child.maxContentHeight)
- child.actualHeight = newHeight
+ child.setFinalActualHeight(newHeight)
} else {
wakeUpCoordinator.setNotificationsVisibleForExpansion(
height >
- lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications,
+ lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications,
/*animate= */ true,
/*increaseSpeed= */ true,
)
@@ -301,7 +301,7 @@ constructor(
anim.duration = animationDuration
anim.addUpdateListener { animation: ValueAnimator ->
// don't use reflection, because the `actualHeight` field may be obfuscated
- child.actualHeight = animation.animatedValue as Int
+ child.setFinalActualHeight(animation.animatedValue as Int)
}
anim.addListener(
object : AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
new file mode 100644
index 000000000000..74faf2576abd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 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.notification
+
+import android.util.Property
+import android.view.View
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.createDefaultSpring
+import com.android.systemui.statusbar.notification.stack.AnimationProperties
+
+/**
+ * A physically animatable property of a view.
+ *
+ * @param tag the view tag to safe this property in
+ * @param property the property to animate.
+ */
+data class PhysicsProperty(val tag: Int, val property: Property<View, Float>) {
+ val offsetProperty =
+ object : FloatPropertyCompat<View>(property.name) {
+ override fun getValue(view: View): Float {
+ return property.get(view)
+ }
+
+ override fun setValue(view: View, offset: Float) {
+ val propertyData = view.getTag(tag) as PropertyData? ?: return
+ propertyData.offset = offset
+ property.set(view, propertyData.finalValue + offset)
+ }
+ }
+
+ fun setFinalValue(view: View, finalValue: Float) {
+ val propertyData = obtainPropertyData(view, this)
+ val previousValue = propertyData.finalValue
+ if (previousValue != finalValue) {
+ propertyData.finalValue = finalValue
+ property.set(view, propertyData.finalValue + propertyData.offset)
+ }
+ }
+}
+
+/** The propertyData associated with each animation running */
+data class PropertyData(
+ var finalValue: Float = 0f,
+ var offset: Float = 0f,
+ var animator: SpringAnimation? = null,
+ var delayRunnable: Runnable? = null,
+)
+
+/**
+ * A utility that can run physics based animations in a simple way. It properly handles overlapping
+ * calls where sometimes a property can be set without animation, while also having instances where
+ * it's supposed to start animations.
+ *
+ * This overall helps making sure that physics based animations complete and don't constantly start
+ * new transitions which can lead to a feeling of lagging behind.
+ *
+ * Overall it is achieved by starting offset animations to an end value as soon as an animation is
+ * requested and updating the end value immediately when no animation is needed. With the offset
+ * always going to 0, this ensures that animations complete within a short time after an animation
+ * has been requested.
+ */
+class PhysicsPropertyAnimator {
+ companion object {
+ @JvmField val TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag
+
+ @JvmField
+ val Y_TRANSLATION: PhysicsProperty =
+ PhysicsProperty(TAG_ANIMATOR_TRANSLATION_Y, View.TRANSLATION_Y)
+
+ // Uses the standard spatial material spring by default
+ @JvmStatic
+ fun createDefaultSpring(): SpringForce {
+ return SpringForce()
+ .setStiffness(380f) // MEDIUM LOW STIFFNESS
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) // LOW BOUNCINESS
+ }
+
+ @JvmStatic
+ @JvmOverloads
+ /**
+ * Set a property on a view, updating its value, even if it's already animating. The @param
+ * animated can be used to request an animation. If the view isn't animated, this utility
+ * will update the current animation if existent, such that the end value will point
+ * to @param newEndValue or apply it directly if there's no animation.
+ */
+ fun setProperty(
+ view: View,
+ animatableProperty: PhysicsProperty,
+ newEndValue: Float,
+ properties: AnimationProperties? = null,
+ animated: Boolean = false,
+ endListener: DynamicAnimation.OnAnimationEndListener? = null,
+ ) {
+ if (animated) {
+ startAnimation(view, animatableProperty, newEndValue, properties, endListener)
+ } else {
+ animatableProperty.setFinalValue(view, newEndValue)
+ }
+ }
+
+ fun isAnimating(view: View, property: PhysicsProperty): Boolean {
+ val (_, _, animator, _) = obtainPropertyData(view, property)
+ return animator?.isRunning ?: false
+ }
+ }
+}
+
+private fun startAnimation(
+ view: View,
+ animatableProperty: PhysicsProperty,
+ newEndValue: Float,
+ properties: AnimationProperties?,
+ endListener: DynamicAnimation.OnAnimationEndListener?,
+) {
+ val property = animatableProperty.property
+ val propertyData = obtainPropertyData(view, animatableProperty)
+ val previousEndValue = propertyData.finalValue
+ if (previousEndValue == newEndValue) {
+ return
+ }
+ propertyData.finalValue = newEndValue
+ var animator = propertyData.animator
+ if (animator == null) {
+ animator = SpringAnimation(view, animatableProperty.offsetProperty)
+ propertyData.animator = animator
+ animator.setSpring(createDefaultSpring())
+ val listener = properties?.getAnimationEndListener(animatableProperty.property)
+ if (listener != null) {
+ animator.addEndListener(listener)
+ // We always notify things as started even if we have a delay
+ properties.getAnimationStartListener(animatableProperty.property)?.accept(animator)
+ }
+ // remove the tag when the animation is finished
+ animator.addEndListener { _, _, _, _ -> propertyData.animator = null }
+ }
+ // TODO(b/393581344): look at custom spring
+ endListener?.let { animator.addEndListener(it) }
+ val newOffset = previousEndValue - newEndValue + propertyData.offset
+
+ // Immedialely set the new offset that compensates for the immediate end value change
+ propertyData.offset = newOffset
+ property.set(view, newEndValue + newOffset)
+
+ // cancel previous starters still pending
+ view.removeCallbacks(propertyData.delayRunnable)
+ animator.setStartValue(newOffset)
+ val startRunnable = Runnable {
+ animator.animateToFinalPosition(0f)
+ propertyData.delayRunnable = null
+ }
+ if (properties != null && properties.delay > 0 && !animator.isRunning) {
+ propertyData.delayRunnable = startRunnable
+ view.postDelayed(propertyData.delayRunnable, properties.delay)
+ } else {
+ startRunnable.run()
+ }
+}
+
+private fun obtainPropertyData(view: View, animatableProperty: PhysicsProperty): PropertyData {
+ var propertyData = view.getTag(animatableProperty.tag) as PropertyData?
+ if (propertyData == null) {
+ propertyData =
+ PropertyData(finalValue = animatableProperty.property.get(view), offset = 0f, null)
+ view.setTag(animatableProperty.tag, propertyData)
+ }
+ return propertyData
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index d383bee64530..b5858ec7e4e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2745,7 +2745,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
top = params.getTop();
}
int actualHeight = params.getBottom() - top;
- setActualHeight(actualHeight);
+ setFinalActualHeight(actualHeight);
int notificationStackTop = params.getNotificationParentTop();
top -= notificationStackTop;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 2bc48746f847..da664f864f06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.Flags.notificationColorUpdateLogger;
+import static com.android.systemui.Flags.physicalNotificationMovement;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
@@ -24,6 +25,7 @@ import android.content.res.Configuration;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.View;
@@ -41,6 +43,7 @@ import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.PhysicsProperty;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
@@ -58,6 +61,20 @@ import java.util.List;
* An abstract view for expandable views.
*/
public abstract class ExpandableView extends FrameLayout implements Dumpable, Roundable {
+ public static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
+ public static final PhysicsProperty HEIGHT_PROPERTY = new PhysicsProperty(TAG_ANIMATOR_HEIGHT,
+ new FloatProperty<>("ActualHeight") {
+
+ @Override
+ public Float get(View view) {
+ return (float) ((ExpandableView) view).getActualHeight();
+ }
+
+ @Override
+ public void setValue(@NonNull View view, float value) {
+ ((ExpandableView) view).setActualHeight((int) value);
+ }
+ });
private static final String TAG = "ExpandableView";
/** whether the dump() for this class should include verbose details */
protected static final boolean DUMP_VERBOSE = Compile.IS_DEBUG
@@ -84,7 +101,8 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
protected float mContentTransformationAmount;
protected boolean mIsLastChild;
protected int mContentShift;
- @NonNull private final ExpandableViewState mViewState;
+ @NonNull
+ private final ExpandableViewState mViewState;
private float mContentTranslation;
protected boolean mLastInSection;
protected boolean mFirstInSection;
@@ -205,7 +223,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
MeasureSpec.EXACTLY);
}
child.measure(getChildMeasureSpec(
- widthMeasureSpec, viewHorizontalPadding, layoutParams.width),
+ widthMeasureSpec, viewHorizontalPadding, layoutParams.width),
childHeightSpec);
int childHeight = child.getMeasuredHeight();
maxChildHeight = Math.max(maxChildHeight, childHeight);
@@ -223,7 +241,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
// Now that we know our own height, measure the children that are MATCH_PARENT
for (View child : mMatchParentViews) {
child.measure(getChildMeasureSpec(
- widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width),
+ widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width),
exactlyOwnHeightSpec);
}
mMatchParentViews.clear();
@@ -269,12 +287,29 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
}
/**
+ * Sets the final value of the actual height, which is to be applied immediately without
+ * animation. This may be different than the current value if we're animating away an offset.
+ */
+ public void setFinalActualHeight(int childHeight) {
+ if (physicalNotificationMovement()) {
+ HEIGHT_PROPERTY.setFinalValue(this, childHeight);
+ } else {
+ setActualHeight(childHeight);
+ }
+ }
+
+ /**
+ * Once the physical notification movement flag is enabled, don't use
+ * this directly as a public method since it may not update the property values and misbehave
+ * during animations. Use #setFinalActualHeight instead.
+ *
* Sets the actual height of this notification. This is different than the laid out
* {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
*
- * @param actualHeight The height of this notification.
+ * @param actualHeight The height of this notification.
* @param notifyListeners Whether the listener should be informed about the change.
*/
+ @Deprecated
public void setActualHeight(int actualHeight, boolean notifyListeners) {
if (mActualHeight != actualHeight) {
mActualHeight = actualHeight;
@@ -285,7 +320,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
}
}
- public void setActualHeight(int actualHeight) {
+ protected void setActualHeight(int actualHeight) {
setActualHeight(actualHeight, true /* notifyListeners */);
}
@@ -748,7 +783,8 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
*
* @return the ExpandableView's view state.
*/
- @NonNull public ExpandableViewState getViewState() {
+ @NonNull
+ public ExpandableViewState getViewState() {
return mViewState;
}
@@ -840,9 +876,10 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
* Set how much this notification is transformed into the shelf.
*
* @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
- * to the content away
- * @param isLastChild is this the last child in the list. If true, then the transformation is
- * different since its content fades out.
+ * to the content away
+ * @param isLastChild is this the last child in the list. If true, then the
+ * transformation is
+ * different since its content fades out.
*/
public void setContentTransformationAmount(float contentTransformationAmount,
boolean isLastChild) {
@@ -971,8 +1008,9 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
public interface OnHeightChangedListener {
/**
- * @param view the view for which the height changed, or {@code null} if just the top
- * padding or the padding between the elements changed
+ * @param view the view for which the height changed, or {@code null} if just the
+ * top
+ * padding or the padding between the elements changed
* @param needsAnimation whether the view height needs to be animated
*/
void onHeightChanged(ExpandableView view, boolean needsAnimation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 00b9aa42ab26..3d60092cf29a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -23,6 +23,8 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+
import java.util.function.Consumer;
/**
@@ -77,6 +79,34 @@ public class AnimationProperties {
}
/**
+ * @return a listener that will be added for a given property during its animation. Similar to
+ * the finish listener but used for Dynamic / SpringAnimations
+ */
+ public DynamicAnimation.OnAnimationEndListener getAnimationEndListener(Property property) {
+ if (mAnimationEndAction == null && mAnimationCancelAction == null) {
+ return null;
+ }
+ Consumer<Property> cancelAction = mAnimationCancelAction;
+ Consumer<Property> endAction = mAnimationEndAction;
+ return (animation, canceled, value, velocity) -> {
+ if (canceled && cancelAction != null) {
+ cancelAction.accept(property);
+ } else if (!canceled && endAction != null) {
+ endAction.accept(property);
+ }
+ };
+ }
+
+ /**
+ * @return a listener that is invoked when a property animation starts, used for dynamic
+ * animations. For classical, interpolator based animations used the listeneradapter instead,
+ * this is only for Dynamic Animations
+ */
+ public Consumer<DynamicAnimation> getAnimationStartListener(Property property) {
+ return null;
+ }
+
+ /**
* Add a callback for animation cancellation.
*/
public AnimationProperties setAnimationCancelAction(Consumer<Property> listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index 69c9a4bf2dbb..8cf9dd365b60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -16,23 +16,31 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.Flags.physicalNotificationMovement;
+import static com.android.systemui.statusbar.notification.row.ExpandableView.HEIGHT_PROPERTY;
+import static com.android.systemui.statusbar.notification.row.ExpandableView.TAG_ANIMATOR_HEIGHT;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.util.FloatProperty;
import android.view.View;
+import androidx.annotation.NonNull;
+
import com.android.app.animation.Interpolators;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.PhysicsProperty;
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
-* A state of an expandable view
-*/
+ * A state of an expandable view
+ */
public class ExpandableViewState extends ViewState {
- private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
private static final int TAG_ANIMATOR_BOTTOM_INSET = R.id.bottom_inset_animator_tag;
private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
@@ -149,7 +157,7 @@ public class ExpandableViewState extends ViewState {
// apply height
if (height != newHeight) {
- expandableView.setActualHeight(newHeight, false /* notifyListeners */);
+ expandableView.setFinalActualHeight(newHeight);
}
// apply hiding sensitive
@@ -186,8 +194,24 @@ public class ExpandableViewState extends ViewState {
// start height animation
if (this.height != expandableView.getActualHeight()) {
- startHeightAnimation(expandableView, properties);
- } else {
+ if (mUsePhysicsForMovement) {
+ boolean animateHeight = properties.getAnimationFilter().animateHeight;
+ if (animateHeight) {
+ expandableView.setActualHeightAnimating(true);
+ }
+ PhysicsPropertyAnimator.setProperty(child, HEIGHT_PROPERTY, this.height, properties,
+ animateHeight,
+ (animation, canceled, value, velocity) -> {
+ expandableView.setActualHeightAnimating(false);
+ if (!canceled && child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setGroupExpansionChanging(
+ false /* isExpansionChanging */);
+ }
+ });
+ } else {
+ startHeightAnimationInterpolator(expandableView, properties);
+ }
+ } else {
abortAnimation(child, TAG_ANIMATOR_HEIGHT);
}
@@ -224,7 +248,8 @@ public class ExpandableViewState extends ViewState {
}
}
- private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
+ private void startHeightAnimationInterpolator(final ExpandableView child,
+ AnimationProperties properties) {
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
int newEndValue = this.height;
@@ -374,38 +399,16 @@ public class ExpandableViewState extends ViewState {
}
});
startAnimator(animator, listener);
- child.setTag(clipTop ? TAG_ANIMATOR_TOP_INSET:TAG_ANIMATOR_BOTTOM_INSET, animator);
- child.setTag(clipTop ? TAG_START_TOP_INSET: TAG_START_BOTTOM_INSET,
+ child.setTag(clipTop ? TAG_ANIMATOR_TOP_INSET : TAG_ANIMATOR_BOTTOM_INSET, animator);
+ child.setTag(clipTop ? TAG_START_TOP_INSET : TAG_START_BOTTOM_INSET,
clipTop ? child.getClipTopAmount() : child.getClipBottomAmount());
- child.setTag(clipTop ? TAG_END_TOP_INSET: TAG_END_BOTTOM_INSET, newEndValue);
- }
-
- /**
- * Get the end value of the height animation running on a view or the actualHeight
- * if no animation is running.
- */
- public static int getFinalActualHeight(ExpandableView view) {
- if (view == null) {
- return 0;
- }
- ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
- if (heightAnimator == null) {
- return view.getActualHeight();
- } else {
- return getChildTag(view, TAG_END_HEIGHT);
- }
+ child.setTag(clipTop ? TAG_END_TOP_INSET : TAG_END_BOTTOM_INSET, newEndValue);
}
@Override
public void cancelAnimations(View view) {
super.cancelAnimations(view);
- Animator animator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
- if (animator != null) {
- animator.cancel();
- }
- animator = getChildTag(view, TAG_ANIMATOR_TOP_INSET);
- if (animator != null) {
- animator.cancel();
- }
+ abortAnimation(view, TAG_ANIMATOR_HEIGHT);
+ abortAnimation(view, TAG_ANIMATOR_TOP_INSET);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index ee57d459e71c..1d185356626b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1352,10 +1352,11 @@ public class NotificationChildrenContainer extends ViewGroup
if (i < maxAllowedVisibleChildren) {
float singleLineHeight = child.getShowingLayout().getMinHeight(
false /* likeGroupExpanded */);
- child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
- childHeight, fraction), false);
+ childHeight = NotificationUtils.interpolate(singleLineHeight,
+ childHeight, fraction);
+ child.setFinalActualHeight((int) childHeight);
} else {
- child.setActualHeight((int) childHeight, false);
+ child.setFinalActualHeight((int) childHeight);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c694a19a46ae..3d60e03d7ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -26,6 +26,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
import static com.android.systemui.Flags.magneticNotificationSwipes;
import static com.android.systemui.Flags.notificationOverExpansionClippingFix;
+import static com.android.systemui.Flags.physicalNotificationMovement;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_DOWN;
@@ -109,6 +110,7 @@ import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -5761,7 +5763,12 @@ public class NotificationStackScrollLayout
+ view.getActualHeight() - mShelf.getIntrinsicHeight();
}
} else if (!firstVisibleView) {
- view.setTranslationY(wakeUplocation);
+ if (physicalNotificationMovement()) {
+ PhysicsPropertyAnimator.setProperty(view, PhysicsPropertyAnimator.Y_TRANSLATION,
+ wakeUplocation);
+ } else {
+ view.setTranslationY(wakeUplocation);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index c783250f2e0a..5e0d57ebb3fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.Flags.physicalNotificationMovement;
+import static com.android.systemui.statusbar.notification.row.ExpandableView.HEIGHT_PROPERTY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_CYCLING_IN;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_CYCLING_OUT;
@@ -29,11 +31,14 @@ import android.content.Context;
import android.util.Property;
import android.view.View;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -41,6 +46,7 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Stack;
+import java.util.function.Consumer;
/**
* An stack state animator which handles animations to new StackScrollStates
@@ -68,8 +74,10 @@ public class StackStateAnimator {
public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
private static final int MAX_STAGGER_COUNT = 5;
- @VisibleForTesting int mGoToFullShadeAppearingTranslation;
- @VisibleForTesting float mHeadsUpAppearStartAboveScreen;
+ @VisibleForTesting
+ int mGoToFullShadeAppearingTranslation;
+ @VisibleForTesting
+ float mHeadsUpAppearStartAboveScreen;
// Padding between the old and new heads up notifications for the hun cycling animation
private float mHeadsUpCyclingPadding;
private final ExpandableViewState mTmpState = new ExpandableViewState();
@@ -80,8 +88,9 @@ public class StackStateAnimator {
private ArrayList<View> mNewAddChildren = new ArrayList<>();
private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
- private HashSet<Animator> mAnimatorSet = new HashSet<>();
+ private HashSet<Object> mAnimatorSet = new HashSet<>();
private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
+ private Stack<DynamicAnimation.OnAnimationEndListener> mAnimationEndPool = new Stack<>();
private AnimationFilter mAnimationFilter = new AnimationFilter();
private long mCurrentLength;
private long mCurrentAdditionalDelay;
@@ -99,6 +108,9 @@ public class StackStateAnimator {
mHostLayout = hostLayout;
initView(context);
mAnimationProperties = new AnimationProperties() {
+
+ private final Consumer<DynamicAnimation> mDynamicAnimationConsumer = mAnimatorSet::add;
+
@Override
public AnimationFilter getAnimationFilter() {
return mAnimationFilter;
@@ -110,6 +122,17 @@ public class StackStateAnimator {
}
@Override
+ public DynamicAnimation.OnAnimationEndListener getAnimationEndListener(
+ Property property) {
+ return getGlobalAnimationEndListener();
+ }
+
+ @Override
+ public Consumer<DynamicAnimation> getAnimationStartListener(Property property) {
+ return mDynamicAnimationConsumer;
+ }
+
+ @Override
public boolean wasAdded(View view) {
return mNewAddChildren.contains(view);
}
@@ -187,11 +210,11 @@ public class StackStateAnimator {
adaptDurationWhenGoingToFullShade(child, viewState, wasAdded, animationStaggerCount);
mAnimationProperties.delay = 0;
if (wasAdded || mAnimationFilter.hasDelays
- && (viewState.getYTranslation() != child.getTranslationY()
- || viewState.getZTranslation() != child.getTranslationZ()
- || viewState.getAlpha() != child.getAlpha()
- || viewState.height != child.getActualHeight()
- || viewState.clipTopAmount != child.getClipTopAmount())) {
+ && (viewState.getYTranslation() != child.getTranslationY()
+ || viewState.getZTranslation() != child.getTranslationZ()
+ || viewState.getAlpha() != child.getAlpha()
+ || viewState.height != child.getActualHeight()
+ || viewState.clipTopAmount != child.getClipTopAmount())) {
mAnimationProperties.delay = mCurrentAdditionalDelay
+ calculateChildAnimationDelay(viewState, animationStaggerCount);
}
@@ -209,7 +232,13 @@ public class StackStateAnimator {
mAnimationProperties.duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50
+ (long) (100 * longerDurationFactor);
}
- child.setTranslationY(viewState.getYTranslation() + startOffset);
+ float newTranslationY = viewState.getYTranslation() + startOffset;
+ if (physicalNotificationMovement()) {
+ PhysicsPropertyAnimator.setProperty(child, PhysicsPropertyAnimator.Y_TRANSLATION,
+ newTranslationY);
+ } else {
+ child.setTranslationY(newTranslationY);
+ }
}
}
@@ -312,7 +341,7 @@ public class StackStateAnimator {
/**
* @return an adapter which ensures that onAnimationFinished is called once no animation is
- * running anymore
+ * running anymore
*/
private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
if (!mAnimationListenerPool.empty()) {
@@ -345,6 +374,27 @@ public class StackStateAnimator {
};
}
+ /**
+ * @return an adapter which ensures that onAnimationFinished is called once no animation is
+ * running anymore
+ */
+ private DynamicAnimation.OnAnimationEndListener getGlobalAnimationEndListener() {
+ if (!mAnimationEndPool.empty()) {
+ return mAnimationEndPool.pop();
+ }
+ return new DynamicAnimation.OnAnimationEndListener() {
+ @Override
+ public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+ float velocity) {
+ mAnimatorSet.remove(animation);
+ if (mAnimatorSet.isEmpty() && !canceled) {
+ onAnimationFinished();
+ }
+ mAnimationEndPool.push(this);
+ }
+ };
+ }
+
private void onAnimationFinished() {
mHostLayout.onChildAnimationFinished();
@@ -358,7 +408,7 @@ public class StackStateAnimator {
* Process the animationEvents for a new animation. Here is the place to do something custom,
* like to modify the ViewState or to create a custom animation for an event.
*
- * @param animationEvents the animation events for the animation to perform
+ * @param animationEvents the animation events for the animation to perform
* @return true if any custom animation was created
*/
private boolean processAnimationEvents(
@@ -428,7 +478,7 @@ public class StackStateAnimator {
translationDirection = ((viewState.getYTranslation()
- (ownPosition + actualHeight / 2.0f)) * 2 /
actualHeight);
- translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
+ translationDirection = Math.max(Math.min(translationDirection, 1.0f), -1.0f);
}
Runnable postAnimation;
@@ -446,7 +496,7 @@ public class StackStateAnimator {
changingView.removeFromTransientContainer();
};
} else {
- startAnimation = ()-> {
+ startAnimation = () -> {
changingView.setInRemovalAnimation(true);
};
postAnimation = () -> {
@@ -460,7 +510,7 @@ public class StackStateAnimator {
ExpandableView.ClipSide.BOTTOM);
needsCustomAnimation = true;
} else if (event.animationType ==
- NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
+ NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
boolean isFullySwipedOut = mHostLayout.isFullySwipedOut(changingView);
if (loggable) {
mLogger.processAnimationEventsRemoveSwipeOut(key, isFullySwipedOut, isHeadsUp);
@@ -699,8 +749,8 @@ public class StackStateAnimator {
/**
* @param headsUpFromBottom Whether we are showing the HUNs at the bottom of the screen
- * @param oldHunHeight Height of the old HUN
- * @param newHunHeight Height of the new HUN
+ * @param oldHunHeight Height of the old HUN
+ * @param newHunHeight Height of the new HUN
* @return The y translation target value of the HUN cycling out animation
*/
private float getHeadsUpCyclingOutYTranslation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index b2ffa4aa8233..2ef6f362af34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.Flags.physicalNotificationMovement;
+import static com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.TAG_ANIMATOR_TRANSLATION_Y;
+import static com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Y_TRANSLATION;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -26,14 +30,20 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
+
import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
+import com.android.systemui.statusbar.notification.PhysicsProperty;
+import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.PropertyData;
import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import java.io.PrintWriter;
import java.lang.reflect.Field;
@@ -46,6 +56,14 @@ import java.lang.reflect.Modifier;
*/
public class ViewState implements Dumpable {
+ public ViewState() {
+ this(physicalNotificationMovement());
+ }
+
+ public ViewState(boolean usePhysicsForMovement) {
+ setUsePhysicsForMovement(usePhysicsForMovement);
+ }
+
/**
* Some animation properties that can be used to update running animations but not creating
* any new ones.
@@ -59,7 +77,6 @@ public class ViewState implements Dumpable {
}
};
private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag;
- private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
private static final int TAG_END_TRANSLATION_X = R.id.translation_x_animator_end_value_tag;
@@ -72,8 +89,7 @@ public class ViewState implements Dumpable {
private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
private static final String LOG_TAG = "StackViewState";
- private static final AnimatableProperty SCALE_X_PROPERTY
- = new AnimatableProperty() {
+ private static final AnimatableProperty SCALE_X_PROPERTY = new AnimatableProperty() {
@Override
public int getAnimationStartTag() {
@@ -96,8 +112,7 @@ public class ViewState implements Dumpable {
}
};
- private static final AnimatableProperty SCALE_Y_PROPERTY
- = new AnimatableProperty() {
+ private static final AnimatableProperty SCALE_Y_PROPERTY = new AnimatableProperty() {
@Override
public int getAnimationStartTag() {
@@ -129,11 +144,16 @@ public class ViewState implements Dumpable {
private float mZTranslation;
private float mScaleX = 1.0f;
private float mScaleY = 1.0f;
+ protected boolean mUsePhysicsForMovement = false;
public float getAlpha() {
return mAlpha;
}
+ public void setUsePhysicsForMovement(boolean usePhysicsForMovement) {
+ this.mUsePhysicsForMovement = usePhysicsForMovement;
+ }
+
/**
* @param alpha View transparency.
*/
@@ -230,6 +250,7 @@ public class ViewState implements Dumpable {
hidden = viewState.hidden;
mScaleX = viewState.mScaleX;
mScaleY = viewState.mScaleY;
+ mUsePhysicsForMovement = viewState.mUsePhysicsForMovement;
}
public void initFrom(View view) {
@@ -261,11 +282,15 @@ public class ViewState implements Dumpable {
}
// apply yTranslation
- boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y);
- if (animatingY) {
- updateAnimationY(view);
- } else if (view.getTranslationY() != this.mYTranslation) {
- view.setTranslationY(this.mYTranslation);
+ if (mUsePhysicsForMovement) {
+ PhysicsPropertyAnimator.setProperty(view, Y_TRANSLATION, this.mYTranslation);
+ } else {
+ boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y);
+ if (animatingY) {
+ updateAnimationY(view);
+ } else if (view.getTranslationY() != this.mYTranslation) {
+ view.setTranslationY(this.mYTranslation);
+ }
}
// apply zTranslation
@@ -293,8 +318,8 @@ public class ViewState implements Dumpable {
}
int oldVisibility = view.getVisibility();
- boolean becomesInvisible = this.mAlpha == 0.0f
- || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
+ boolean becomesInvisible = this.mAlpha == 0.0f || (this.hidden && (!isAnimating(view)
+ || oldVisibility != View.VISIBLE));
boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
if (animatingAlpha) {
updateAlphaAnimation(view);
@@ -315,9 +340,8 @@ public class ViewState implements Dumpable {
} else {
boolean newLayerTypeIsHardware = becomesFaded && view.hasOverlappingRendering();
int layerType = view.getLayerType();
- int newLayerType = newLayerTypeIsHardware
- ? View.LAYER_TYPE_HARDWARE
- : View.LAYER_TYPE_NONE;
+ int newLayerType =
+ newLayerTypeIsHardware ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
if (layerType != newLayerType) {
view.setLayerType(newLayerType, null);
}
@@ -360,11 +384,19 @@ public class ViewState implements Dumpable {
}
private static boolean isAnimating(View view, int tag) {
- return getChildTag(view, tag) != null;
+ Object childTag = getChildTag(view, tag);
+ if (childTag instanceof PropertyData propertyData) {
+ return propertyData.getAnimator() != null;
+ }
+ return childTag != null;
}
public static boolean isAnimating(View view, AnimatableProperty property) {
- return getChildTag(view, property.getAnimatorTag()) != null;
+ Object childTag = getChildTag(view, property.getAnimatorTag());
+ if (childTag instanceof PropertyData propertyData) {
+ return propertyData.getAnimator() != null;
+ }
+ return childTag != null;
}
/**
@@ -376,8 +408,7 @@ public class ViewState implements Dumpable {
public void animateTo(View child, AnimationProperties animationProperties) {
boolean wasVisible = child.getVisibility() == View.VISIBLE;
final float alpha = this.mAlpha;
- if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
- && !this.gone && !this.hidden) {
+ if (!wasVisible && (alpha != 0 || child.getAlpha() != 0) && !this.gone && !this.hidden) {
child.setVisibility(View.VISIBLE);
}
float childAlpha = child.getAlpha();
@@ -465,8 +496,8 @@ public class ViewState implements Dumpable {
}
}
- ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
- child.getAlpha(), newEndValue);
+ ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, child.getAlpha(),
+ newEndValue);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
// Handle layer type
child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
@@ -516,8 +547,7 @@ public class ViewState implements Dumpable {
startZTranslationAnimation(view, NO_NEW_ANIMATIONS);
}
- private void updateAnimation(View view, AnimatableProperty property,
- float endValue) {
+ private void updateAnimation(View view, AnimatableProperty property, float endValue) {
PropertyAnimator.startAnimation(view, property, endValue, NO_NEW_ANIMATIONS);
}
@@ -615,8 +645,8 @@ public class ViewState implements Dumpable {
child.getTranslationX(), newEndValue);
Interpolator customInterpolator = properties.getCustomInterpolator(child,
View.TRANSLATION_X);
- Interpolator interpolator = customInterpolator != null ? customInterpolator
- : Interpolators.FAST_OUT_SLOW_IN;
+ Interpolator interpolator =
+ customInterpolator != null ? customInterpolator : Interpolators.FAST_OUT_SLOW_IN;
animator.setInterpolator(interpolator);
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
animator.setDuration(newDuration);
@@ -649,6 +679,24 @@ public class ViewState implements Dumpable {
}
private void startYTranslationAnimation(final View child, AnimationProperties properties) {
+ if (mUsePhysicsForMovement) {
+ // Y Translation does some extra calls when it ends, so lets add a listener
+ DynamicAnimation.OnAnimationEndListener endListener =
+ (animation, canceled, value, velocity) -> {
+ if (!canceled) {
+ HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(child, false);
+ onYTranslationAnimationFinished(child);
+ }
+ };
+ PhysicsPropertyAnimator.setProperty(child, Y_TRANSLATION, this.mYTranslation,
+ properties, properties.getAnimationFilter().animateY, endListener);
+ } else {
+ startYTranslationInterpolatorAnimation(child, properties);
+ }
+ }
+
+ private void startYTranslationInterpolatorAnimation(View child,
+ AnimationProperties properties) {
Float previousStartValue = getChildTag(child, TAG_START_TRANSLATION_Y);
Float previousEndValue = getChildTag(child, TAG_END_TRANSLATION_Y);
float newEndValue = this.mYTranslation;
@@ -681,8 +729,8 @@ public class ViewState implements Dumpable {
child.getTranslationY(), newEndValue);
Interpolator customInterpolator = properties.getCustomInterpolator(child,
View.TRANSLATION_Y);
- Interpolator interpolator = customInterpolator != null ? customInterpolator
- : Interpolators.FAST_OUT_SLOW_IN;
+ Interpolator interpolator =
+ customInterpolator != null ? customInterpolator : Interpolators.FAST_OUT_SLOW_IN;
animator.setInterpolator(interpolator);
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
animator.setDuration(newDuration);
@@ -731,9 +779,19 @@ public class ViewState implements Dumpable {
}
protected void abortAnimation(View child, int animatorTag) {
- Animator previousAnimator = getChildTag(child, animatorTag);
- if (previousAnimator != null) {
- previousAnimator.cancel();
+ Object storedTag = getChildTag(child, animatorTag);
+ if (storedTag != null) {
+ if (storedTag instanceof Animator animator) {
+ animator.cancel();
+ } else if (storedTag instanceof PropertyData propertyData) {
+ // Physics based animation!
+ Runnable delayRunnable = propertyData.getDelayRunnable();
+ child.removeCallbacks(delayRunnable);
+ SpringAnimation animator = propertyData.getAnimator();
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
}
}
@@ -750,46 +808,15 @@ public class ViewState implements Dumpable {
if (previousAnimator != null) {
// We take either the desired length of the new animation or the remaining time of
// the previous animator, whichever is longer.
- newDuration = Math.max(previousAnimator.getDuration()
- - previousAnimator.getCurrentPlayTime(), newDuration);
+ newDuration = Math.max(
+ previousAnimator.getDuration() - previousAnimator.getCurrentPlayTime(),
+ newDuration);
previousAnimator.cancel();
}
return newDuration;
}
/**
- * Get the end value of the xTranslation animation running on a view or the xTranslation
- * if no animation is running.
- */
- public static float getFinalTranslationX(View view) {
- if (view == null) {
- return 0;
- }
- ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
- if (xAnimator == null) {
- return view.getTranslationX();
- } else {
- return getChildTag(view, TAG_END_TRANSLATION_X);
- }
- }
-
- /**
- * Get the end value of the yTranslation animation running on a view or the yTranslation
- * if no animation is running.
- */
- public static float getFinalTranslationY(View view) {
- if (view == null) {
- return 0;
- }
- ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
- if (yAnimator == null) {
- return view.getTranslationY();
- } else {
- return getChildTag(view, TAG_END_TRANSLATION_Y);
- }
- }
-
- /**
* Get the end value of the zTranslation animation running on a view or the zTranslation
* if no animation is running.
*/
@@ -806,26 +833,14 @@ public class ViewState implements Dumpable {
}
public static boolean isAnimatingY(View child) {
- return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
+ return isAnimating(child, TAG_ANIMATOR_TRANSLATION_Y);
}
public void cancelAnimations(View view) {
- Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
- if (animator != null) {
- animator.cancel();
- }
- animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
- if (animator != null) {
- animator.cancel();
- }
- animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
- if (animator != null) {
- animator.cancel();
- }
- animator = getChildTag(view, TAG_ANIMATOR_ALPHA);
- if (animator != null) {
- animator.cancel();
- }
+ abortAnimation(view, TAG_ANIMATOR_TRANSLATION_X);
+ abortAnimation(view, TAG_ANIMATOR_TRANSLATION_Y);
+ abortAnimation(view, TAG_ANIMATOR_TRANSLATION_Z);
+ abortAnimation(view, TAG_ANIMATOR_ALPHA);
}
@Override
@@ -840,8 +855,8 @@ public class ViewState implements Dumpable {
// Print field names paired with their values
for (Field field : fields) {
int modifiers = field.getModifiers();
- if (Modifier.isStatic(modifiers) || field.isSynthetic()
- || Modifier.isTransient(modifiers)) {
+ if (Modifier.isStatic(modifiers) || field.isSynthetic() || Modifier.isTransient(
+ modifiers)) {
continue;
}
if (!first) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 4825a10e901b..15d73d2deb7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -735,6 +735,7 @@ public class NotificationIconContainer extends ViewGroup {
private final Consumer<Property> mCannedAnimationEndListener;
public IconState(View child) {
+ super(false /* usePhysicsForMovement */);
mView = child;
mCannedAnimationEndListener = (property) -> {
// If we finished animating out of the shelf
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 144939d1086f..38c0d281b320 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -443,6 +443,11 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
}
public static class StatusIconState extends ViewState {
+
+ public StatusIconState() {
+ super(false /* usePhysicsForMovement */);
+ }
+
/// StatusBarIconView.STATE_*
public int visibleState = STATE_ICON;
public boolean justAdded = true;