From 4a0cad571a368047071f58925df7c87919d4592e Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Thu, 2 Dec 2021 16:34:19 +0100 Subject: [PATCH] Disable unfold animation when animations disabled When AnimatorScale is set to zero (possible from settings), the fold animation is disabled. The change to UnfoldLightRevealOverlayAnimation is needed because the overlay view is added independently from UnfoldTransitionProgressProvider callbacks every time the internal screen is turned on. Test: Tested on device && atest com.android.systemui.unfold.util.ScaleAwareUnfoldProgressProviderTest Bug: 208791440 Change-Id: Icda9ed6d708873768c4ac533b9c790ae1631b45c --- .../unfold/UnfoldTransitionFactory.kt | 7 +- .../NaturalRotationUnfoldProgressProvider.kt | 1 - .../ScaleAwareTransitionProgressProvider.kt | 50 +++++++ .../UnfoldLightRevealOverlayAnimation.kt | 3 +- .../ScaleAwareUnfoldProgressProviderTest.kt | 139 ++++++++++++++++++ 5 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index b6be6edc7a108..e46b6f12e4a38 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -26,6 +26,7 @@ import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider +import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider import com.android.systemui.unfold.updates.DeviceFoldStateProvider import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider @@ -62,7 +63,7 @@ fun createUnfoldTransitionProgressProvider( mainExecutor ) - return if (config.isHingeAngleEnabled) { + val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) { PhysicsBasedUnfoldTransitionProgressProvider( mainHandler, foldStateProvider @@ -70,6 +71,10 @@ fun createUnfoldTransitionProgressProvider( } else { FixedTimingTransitionProgressProvider(foldStateProvider) } + return ScaleAwareTransitionProgressProvider( + unfoldTransitionProgressProvider, + context.contentResolver + ) } fun createConfig(context: Context): UnfoldTransitionConfig = diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt index e072d41e4eeec..58d7dfb133a59 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt @@ -2,7 +2,6 @@ package com.android.systemui.unfold.util import android.content.Context import android.os.RemoteException -import android.util.Log import android.view.IRotationWatcher import android.view.IWindowManager import android.view.Surface diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt new file mode 100644 index 0000000000000..df9078a15520e --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt @@ -0,0 +1,50 @@ +package com.android.systemui.unfold.util + +import android.animation.ValueAnimator +import android.content.ContentResolver +import android.database.ContentObserver +import android.provider.Settings +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener + +/** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */ +class ScaleAwareTransitionProgressProvider( + unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider, + private val contentResolver: ContentResolver +) : UnfoldTransitionProgressProvider { + + private val scopedUnfoldTransitionProgressProvider = + ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider) + + private val animatorDurationScaleObserver = object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + onAnimatorScaleChanged() + } + } + + init { + contentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), + /* notifyForDescendants= */ false, + animatorDurationScaleObserver) + onAnimatorScaleChanged() + } + + private fun onAnimatorScaleChanged() { + val animationsEnabled = ValueAnimator.areAnimatorsEnabled() + scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(animationsEnabled) + } + + override fun addCallback(listener: TransitionProgressListener) { + scopedUnfoldTransitionProgressProvider.addCallback(listener) + } + + override fun removeCallback(listener: TransitionProgressListener) { + scopedUnfoldTransitionProgressProvider.removeCallback(listener) + } + + override fun destroy() { + contentResolver.unregisterContentObserver(animatorDurationScaleObserver) + scopedUnfoldTransitionProgressProvider.destroy() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt index 51de132108bef..e6fc49fae3156 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.unfold +import android.animation.ValueAnimator import android.content.Context import android.graphics.PixelFormat import android.hardware.devicestate.DeviceStateManager @@ -111,7 +112,7 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor( Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn") try { // Add the view only if we are unfolding and this is the first screen on - if (!isFolded && !isUnfoldHandled) { + if (!isFolded && !isUnfoldHandled && ValueAnimator.areAnimatorsEnabled()) { addView(onOverlayReady) isUnfoldHandled = true } else { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt new file mode 100644 index 0000000000000..db7a85166807c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2021 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.unfold.util + +import android.animation.ValueAnimator +import android.content.ContentResolver +import android.database.ContentObserver +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.util.mockito.any +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { + + @Mock + lateinit var contentResolver: ContentResolver + + @Mock + lateinit var sourceProvider: UnfoldTransitionProgressProvider + + @Mock + lateinit var sinkProvider: TransitionProgressListener + + lateinit var progressProvider: ScaleAwareTransitionProgressProvider + + private val sourceProviderListenerCaptor = + ArgumentCaptor.forClass(TransitionProgressListener::class.java) + + private val animatorDurationScaleListenerCaptor = + ArgumentCaptor.forClass(ContentObserver::class.java) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + progressProvider = ScaleAwareTransitionProgressProvider( + sourceProvider, + contentResolver + ) + + verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture()) + verify(contentResolver).registerContentObserver(any(), any(), + animatorDurationScaleListenerCaptor.capture()) + + progressProvider.addCallback(sinkProvider) + } + + @Test + fun onTransitionStarted_animationsEnabled_eventReceived() { + setAnimationsEnabled(true) + + source.onTransitionStarted() + + verify(sinkProvider).onTransitionStarted() + } + + @Test + fun onTransitionStarted_animationsNotEnabled_eventNotReceived() { + setAnimationsEnabled(false) + + source.onTransitionStarted() + + verifyNoMoreInteractions(sinkProvider) + } + + @Test + fun onTransitionEnd_animationsEnabled_eventReceived() { + setAnimationsEnabled(true) + + source.onTransitionFinished() + + verify(sinkProvider).onTransitionFinished() + } + + @Test + fun onTransitionEnd_animationsNotEnabled_eventNotReceived() { + setAnimationsEnabled(false) + + source.onTransitionFinished() + + verifyNoMoreInteractions(sinkProvider) + } + + @Test + fun onTransitionProgress_animationsEnabled_eventReceived() { + setAnimationsEnabled(true) + + source.onTransitionProgress(42f) + + verify(sinkProvider).onTransitionProgress(42f) + } + + @Test + fun onTransitionProgress_animationsNotEnabled_eventNotReceived() { + setAnimationsEnabled(false) + + source.onTransitionProgress(42f) + + verifyNoMoreInteractions(sinkProvider) + } + + private fun setAnimationsEnabled(enabled: Boolean) { + val durationScale = if (enabled) { + 1f + } else { + 0f + } + ValueAnimator.setDurationScale(durationScale) + animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false) + } + + private val source: TransitionProgressListener + get() = sourceProviderListenerCaptor.value +}