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

Commit 014ef53e authored by Nick Chameyev's avatar Nick Chameyev
Browse files

[Unfold animation] Disable task animations when folding by default

Disables animation of the tasks (cropping and scaling) when
folding e.g. in an app or in split screen. We will still play
the animation when unfolding but we disable it when folding.

Bug: 283218963
Test: atest UnfoldOnlyProgressProviderTest
Test: manual open an app, fold, unfold => there is task unfold animation
Test: manual open an app, start folding => there is no animation
Test: manual fold/unfold with split screen, on launcher
Change-Id: Ide43b31fce8bb0d0aa4881c951590ccc5f8e345f
parent c42b01e3
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
import java.util.concurrent.Executor

/**
 * [UnfoldTransitionProgressProvider] that emits transition progress only when unfolding but not
 * when folding, so we can play the animation only one way but not the other way.
 */
class UnfoldOnlyProgressProvider(
    foldProvider: FoldProvider,
    @Main private val executor: Executor,
    private val sourceProvider: UnfoldTransitionProgressProvider,
    private val scopedProvider: ScopedUnfoldTransitionProgressProvider =
        ScopedUnfoldTransitionProgressProvider(sourceProvider)
) : UnfoldTransitionProgressProvider by scopedProvider {

    private var isFolded = false

    init {
        foldProvider.registerCallback(FoldListener(), executor)
        sourceProvider.addCallback(SourceTransitionListener())
    }

    private inner class SourceTransitionListener : TransitionProgressListener {
        override fun onTransitionFinished() {
            // Disable scoped progress provider after the first unfold animation, so fold animation
            // will not be propagated. It will be re-enabled after folding so we can play
            // the unfold animation again.
            if (!isFolded) {
                scopedProvider.setReadyToHandleTransition(false)
            }
        }
    }

    private inner class FoldListener : FoldCallback {
        override fun onFoldUpdated(isFolded: Boolean) {
            if (isFolded) {
                scopedProvider.setReadyToHandleTransition(true)
            }

            this@UnfoldOnlyProgressProvider.isFolded = isFolded
        }
    }
}
+42 −7
Original line number Diff line number Diff line
@@ -18,16 +18,19 @@ package com.android.systemui.unfold

import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.SystemProperties
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.util.UnfoldOnlyProgressProvider
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
@@ -37,6 +40,7 @@ import dagger.Provides
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Named
import javax.inject.Provider
import javax.inject.Singleton

@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class])
@@ -90,6 +94,18 @@ class UnfoldTransitionModule {
            NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, provider)
        }

    @Provides
    @Singleton
    @Named(UNFOLD_ONLY_PROVIDER)
    fun provideUnfoldOnlyProvider(
        foldProvider: FoldProvider,
        @Main executor: Executor,
        sourceProvider: Optional<UnfoldTransitionProgressProvider>
    ): Optional<UnfoldTransitionProgressProvider> =
        sourceProvider.map { provider ->
            UnfoldOnlyProgressProvider(foldProvider, executor, provider)
        }

    @Provides
    @Named(UNFOLD_STATUS_BAR)
    @Singleton
@@ -102,12 +118,25 @@ class UnfoldTransitionModule {
    @Singleton
    fun provideShellProgressProvider(
        config: UnfoldTransitionConfig,
        provider: Optional<UnfoldTransitionProgressProvider>
    ): ShellUnfoldProgressProvider =
        if (config.isEnabled && provider.isPresent) {
            UnfoldProgressProvider(provider.get())
        provider: Provider<Optional<UnfoldTransitionProgressProvider>>,
        @Named(UNFOLD_ONLY_PROVIDER)
        unfoldOnlyProvider: Provider<Optional<UnfoldTransitionProgressProvider>>
    ): ShellUnfoldProgressProvider {
        val resultingProvider =
            if (config.isEnabled) {
                // Return unfold only provider to the shell if we don't want to animate tasks during
                // folding. Shell provider listeners are responsible for animating task bounds.
                if (ENABLE_FOLD_TASK_ANIMATIONS) {
                    provider
                } else {
            ShellUnfoldProgressProvider.NO_PROVIDER
                    unfoldOnlyProvider
                }
            } else {
                null
            }

        return resultingProvider?.get()?.orElse(null)?.let(::UnfoldProgressProvider)
            ?: ShellUnfoldProgressProvider.NO_PROVIDER
    }

    @Provides
@@ -115,3 +144,9 @@ class UnfoldTransitionModule {
}

const val UNFOLD_STATUS_BAR = "unfold_status_bar"
const val UNFOLD_ONLY_PROVIDER = "unfold_only_provider"

// TODO: b/265764985 - tracking bug to clean-up the flag
// FeatureFlags are not accessible here because it's a global submodule (see GlobalModule.java)
private val ENABLE_FOLD_TASK_ANIMATIONS =
    SystemProperties.getBoolean("persist.unfold.enable_fold_tasks_animation", false)
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr

class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {

    private val listeners = arrayListOf<TransitionProgressListener>()
    private val listeners = mutableListOf<TransitionProgressListener>()

    override fun destroy() {
        listeners.clear()
+1 −18
Original line number Diff line number Diff line
@@ -20,10 +20,9 @@ import android.os.Vibrator
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.util.TestFoldProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -115,20 +114,4 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {

        verify(vibrator).vibrate(any<VibrationEffect>())
    }

    private class TestFoldProvider : FoldProvider {
        private val listeners = arrayListOf<FoldProvider.FoldCallback>()

        override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
            listeners += callback
        }

        override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
            listeners -= callback
        }

        fun onFoldUpdate(isFolded: Boolean) {
            listeners.forEach { it.onFoldUpdated(isFolded) }
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -74,6 +74,11 @@ class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionPr
        currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.")
    }

    fun clear() {
        currentRecording = null
        recordings.clear()
    }

    class UnfoldTransitionRecording {
        private val progressHistory: MutableList<Float> = arrayListOf()
        private var finishingInvocations: Int = 0
Loading