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
This commit is contained in:
Nicolo' Mazzucato
2021-12-02 16:34:19 +01:00
committed by Nicolò Mazzucato
parent f5fe79dedc
commit 4a0cad571a
5 changed files with 197 additions and 3 deletions

View File

@@ -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 =

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -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
}