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:
committed by
Nicolò Mazzucato
parent
f5fe79dedc
commit
4a0cad571a
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user