Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a0cad57 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato Committed by Nicolò Mazzucato
Browse files

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
parent f5fe79de
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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 =
+0 −1
Original line number Diff line number Diff line
@@ -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
+50 −0
Original line number Diff line number Diff line
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()
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -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 {
+139 −0
Original line number Diff line number Diff line
/*
 * 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
}