diff options
8 files changed, 172 insertions, 35 deletions
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt index 3efdc5acb00c..4931b257ad18 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt @@ -192,4 +192,4 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor( ) } -private const val TRANSLATION_PERCENTAGE = 0.3f +private const val TRANSLATION_PERCENTAGE = 0.08f diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index a2c91c7ca531..f68413ab715e 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -694,6 +694,12 @@ object Flags { val ENABLE_DARK_VIGNETTE_WHEN_FOLDING = unreleasedFlag(2700, "enable_dark_vignette_when_folding") + // TODO(b/265764985): Tracking Bug + @Keep + @JvmField + val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS = + unreleasedFlag(2701, "enable_unfold_status_bar_animations") + // TODO(b259590361): Tracking bug val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 9a5d1b5514f5..62d302f2a592 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -26,6 +26,8 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import com.android.systemui.Gefingerpoken import com.android.systemui.R +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeLogger import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator @@ -215,6 +217,7 @@ class PhoneStatusBarViewController private constructor( private val unfoldComponent: Optional<SysUIUnfoldComponent>, @Named(UNFOLD_STATUS_BAR) private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>, + private val featureFlags: FeatureFlags, private val userChipViewModel: StatusBarUserChipViewModel, private val centralSurfaces: CentralSurfaces, private val shadeController: ShadeController, @@ -224,17 +227,25 @@ class PhoneStatusBarViewController private constructor( ) { fun create( view: PhoneStatusBarView - ) = - PhoneStatusBarViewController( - view, - progressProvider.getOrNull(), - centralSurfaces, - shadeController, - shadeLogger, - unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(), - userChipViewModel, - viewUtil, - configurationController + ): PhoneStatusBarViewController { + val statusBarMoveFromCenterAnimationController = + if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) { + unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController() + } else { + null + } + + return PhoneStatusBarViewController( + view, + progressProvider.getOrNull(), + centralSurfaces, + shadeController, + shadeLogger, + statusBarMoveFromCenterAnimationController, + userChipViewModel, + viewUtil, + configurationController ) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt index 19a0866cd0a8..6f2ac2e5059a 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt @@ -87,6 +87,7 @@ constructor( private var isFolded: Boolean = false private var isUnfoldHandled: Boolean = true private var overlayAddReason: AddOverlayReason? = null + private var isTouchBlocked: Boolean = true private var currentRotation: Int = context.display!!.rotation @@ -253,7 +254,15 @@ constructor( params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS params.fitInsetsTypes = 0 - params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + + val touchFlags = + if (isTouchBlocked) { + // Touchable by default, so it will block the touches + 0 + } else { + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + } + params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or touchFlags params.setTrustedOverlay() val packageName: String = context.opPackageName @@ -262,6 +271,24 @@ constructor( return params } + private fun updateTouchBlockIfNeeded(progress: Float) { + // When unfolding unblock touches a bit earlier than the animation end as the + // interpolation has a long tail of very slight movement at the end which should not + // affect much the usage of the device + val shouldBlockTouches = + if (overlayAddReason == UNFOLD) { + progress < UNFOLD_BLOCK_TOUCHES_UNTIL_PROGRESS + } else { + true + } + + if (isTouchBlocked != shouldBlockTouches) { + isTouchBlocked = shouldBlockTouches + + traceSection("$TAG#relayoutToUpdateTouch") { root?.relayout(getLayoutParams()) } + } + } + private fun createLightRevealEffect(): LightRevealEffect { val isVerticalFold = currentRotation == Surface.ROTATION_0 || currentRotation == Surface.ROTATION_180 @@ -288,7 +315,10 @@ constructor( private inner class TransitionListener : TransitionProgressListener { override fun onTransitionProgress(progress: Float) { - executeInBackground { scrimView?.revealAmount = calculateRevealAmount(progress) } + executeInBackground { + scrimView?.revealAmount = calculateRevealAmount(progress) + updateTouchBlockIfNeeded(progress) + } } override fun onTransitionFinished() { @@ -360,5 +390,7 @@ constructor( // constants for revealAmount. const val TRANSPARENT = 1f const val BLACK = 0f + + private const val UNFOLD_BLOCK_TOUCHES_UNTIL_PROGRESS = 0.8f } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt index 6a68b71f639b..8841f481695d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt @@ -65,8 +65,8 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { // Positive translationX -> translated to the right // 10x10 view center is 25px from the center, // When progress is 0.5 it should be translated at: - // 25 * 0.3 * (1 - 0.5) = 3.75px - assertThat(view.translationX).isWithin(0.01f).of(3.75f) + // 25 * 0.08 * (1 - 0.5) = 1px + assertThat(view.translationX).isWithin(0.01f).of(1.0f) } @Test @@ -81,8 +81,8 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { // Positive translationX -> translated to the right // 10x10 view center is 25px from the center, // When progress is 0 it should be translated at: - // 25 * 0.3 * (1 - 0) = 7.5px - assertThat(view.translationX).isWithin(0.01f).of(7.5f) + // 25 * 0.08 * (1 - 0) = 7.5px + assertThat(view.translationX).isWithin(0.01f).of(2f) } @Test @@ -97,7 +97,7 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { // Positive translationX -> translated to the right // 10x10 view center is 25px from the center, // When progress is 1 it should be translated at: - // 25 * 0.3 * 0 = 0px + // 25 * 0.08 * 0 = 0px assertThat(view.translationX).isEqualTo(0f) } @@ -113,8 +113,8 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { // Positive translationX -> translated to the right, original translation is ignored // 10x10 view center is 25px from the center, // When progress is 0.5 it should be translated at: - // 25 * 0.3 * (1 - 0.5) = 3.75px - assertThat(view.translationX).isWithin(0.01f).of(3.75f) + // 25 * 0.08 * (1 - 0.5) = 1px + assertThat(view.translationX).isWithin(0.01f).of(1.0f) } @Test @@ -154,7 +154,7 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { animator.onTransitionProgress(0.5f) // Positive translationY -> translated to the bottom - assertThat(view.translationY).isWithin(0.01f).of(3.75f) + assertThat(view.translationY).isWithin(0.01f).of(1f) } @Test @@ -169,7 +169,7 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { animator.updateViewPositions() // Negative translationX -> translated to the left - assertThat(view.translationX).isWithin(0.1f).of(-5.25f) + assertThat(view.translationX).isWithin(0.1f).of(-1.4f) } private fun createView( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index cdc989897c8e..3edf33b9d3a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -25,6 +25,8 @@ import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.shade.ShadeControllerImpl import com.android.systemui.shade.ShadeLogger @@ -34,6 +36,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.view.ViewUtil import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -54,6 +57,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock + private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController @Mock private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent @@ -93,6 +98,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Test fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() { + whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) + .thenReturn(true) val view = createViewMock() val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java) unfoldConfig.isEnabled = true @@ -108,6 +115,20 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { } @Test + fun onViewAttachedAndDrawn_statusBarAnimationDisabled_animationNotInitialized() { + whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) + .thenReturn(false) + val view = createViewMock() + unfoldConfig.isEnabled = true + // create the controller on main thread as it requires main looper + InstrumentationRegistry.getInstrumentation().runOnMainSync { + controller = createAndInitController(view) + } + + verify(moveFromCenterAnimation, never()).onViewsReady(any()) + } + + @Test fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() { `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false) val returnVal = view.onTouchEvent( @@ -179,6 +200,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { return PhoneStatusBarViewController.Factory( Optional.of(sysuiUnfoldComponent), Optional.of(progressProvider), + featureFlags, userChipViewModel, centralSurfacesImpl, shadeControllerImpl, diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt index 0413d92b6abb..9fe2f5694dde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt @@ -40,7 +40,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { @Before fun setUp() { - progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider) + progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(context, foldStateProvider) progressProvider.addCallback(listener) } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 28e493651137..f8f168bd4dc1 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -15,8 +15,15 @@ */ package com.android.systemui.unfold.progress +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.content.Context import android.os.Trace +import android.util.FloatProperty import android.util.Log +import android.view.animation.AnimationUtils.loadInterpolator import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringAnimation @@ -34,14 +41,22 @@ import com.android.systemui.unfold.updates.name import javax.inject.Inject /** Maps fold updates to unfold transition progress using DynamicAnimation. */ -class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( - private val foldStateProvider: FoldStateProvider -) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener { +class PhysicsBasedUnfoldTransitionProgressProvider +@Inject +constructor(context: Context, private val foldStateProvider: FoldStateProvider) : + UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener { + + private val emphasizedInterpolator = + loadInterpolator(context, android.R.interpolator.fast_out_extra_slow_in) + + private var cannedAnimator: ValueAnimator? = null private val springAnimation = - SpringAnimation(this, AnimationProgressProperty).apply { - addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider) - } + SpringAnimation( + this, + FloatPropertyCompat.createFloatPropertyCompat(AnimationProgressProperty) + ) + .apply { addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider) } private var isTransitionRunning = false private var isAnimatedCancelRunning = false @@ -76,7 +91,8 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( override fun onFoldUpdate(@FoldUpdate update: Int) { when (update) { - FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> { + FOLD_UPDATE_FINISH_FULL_OPEN, + FOLD_UPDATE_FINISH_HALF_OPEN -> { // Do not cancel if we haven't started the transition yet. // This could happen when we fully unfolded the device before the screen // became available. In this case we start and immediately cancel the animation @@ -100,6 +116,14 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( // the transition continues running. if (isAnimatedCancelRunning) { isAnimatedCancelRunning = false + + // Switching to spring animation, start the animation if it + // is not running already + springAnimation.animateToFinalPosition(1.0f) + + cannedAnimator?.removeAllListeners() + cannedAnimator?.cancel() + cannedAnimator = null } } else { startTransition(startValue = 1f) @@ -130,13 +154,22 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( } isAnimatedCancelRunning = true - springAnimation.animateToFinalPosition(endValue) + + if (USE_CANNED_ANIMATION) { + startCannedCancelAnimation() + } else { + springAnimation.animateToFinalPosition(endValue) + } } else { transitionProgress = endValue isAnimatedCancelRunning = false isTransitionRunning = false springAnimation.cancel() + cannedAnimator?.removeAllListeners() + cannedAnimator?.cancel() + cannedAnimator = null + listeners.forEach { it.onTransitionFinished() } if (DEBUG) { @@ -157,7 +190,7 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( } private fun onStartTransition() { - Trace.beginSection( "$TAG#onStartTransition") + Trace.beginSection("$TAG#onStartTransition") listeners.forEach { it.onTransitionStarted() } Trace.endSection() @@ -195,8 +228,39 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( listeners.remove(listener) } + private fun startCannedCancelAnimation() { + cannedAnimator?.cancel() + cannedAnimator = null + + // Temporary remove listener to cancel the spring animation without + // finishing the transition + springAnimation.removeEndListener(this) + springAnimation.cancel() + springAnimation.addEndListener(this) + + cannedAnimator = + ObjectAnimator.ofFloat(this, AnimationProgressProperty, transitionProgress, 1f).apply { + addListener(CannedAnimationListener()) + duration = + (CANNED_ANIMATION_DURATION_MS.toFloat() * (1f - transitionProgress)).toLong() + interpolator = emphasizedInterpolator + start() + } + } + + private inner class CannedAnimationListener : AnimatorListenerAdapter() { + override fun onAnimationStart(animator: Animator) { + Trace.beginAsyncSection("$TAG#cannedAnimatorRunning", 0) + } + + override fun onAnimationEnd(animator: Animator) { + cancelTransition(1f, animate = false) + Trace.endAsyncSection("$TAG#cannedAnimatorRunning", 0) + } + } + private object AnimationProgressProperty : - FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") { + FloatProperty<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") { override fun setValue( provider: PhysicsBasedUnfoldTransitionProgressProvider, @@ -205,7 +269,7 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( provider.transitionProgress = value } - override fun getValue(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float = + override fun get(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float = provider.transitionProgress } } @@ -213,6 +277,8 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider" private const val DEBUG = true +private const val USE_CANNED_ANIMATION = true +private const val CANNED_ANIMATION_DURATION_MS = 1000 private const val SPRING_STIFFNESS = 600.0f private const val MINIMAL_VISIBLE_CHANGE = 0.001f private const val FINAL_HINGE_ANGLE_POSITION = 165f |