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

Commit 57a83348 authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Android (Google) Code Review
Browse files

Merge "Provide unfold progress in main thread wrapping background provider" into main

parents 5a5cf0a1 3407b93a
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.SystemProperties
import com.android.systemui.CoreStartable
import com.android.systemui.Flags
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.dagger.UnfoldBgProgressFlag
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
@@ -64,6 +66,10 @@ class UnfoldTransitionModule {

    @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"

    @Provides
    @UnfoldBgProgressFlag
    fun unfoldBgProgressFlag() = Flags.unfoldAnimationBackgroundProgress()

    /** A globally available FoldStateListener that allows one to query the fold state. */
    @Provides
    @Singleton
+96 −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.progress

import android.os.Looper
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.TestUnfoldTransitionProvider
import com.android.systemui.utils.os.FakeHandler
import kotlin.test.Test
import org.junit.runner.RunWith

@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {

    private val wrappedProgressProvider = TestUnfoldTransitionProvider()
    private val fakeHandler = FakeHandler(Looper.getMainLooper())
    private val listener = TestUnfoldProgressListener()

    private val progressProvider =
        MainThreadUnfoldTransitionProgressProvider(fakeHandler, wrappedProgressProvider)

    @Test
    fun onTransitionStarted_propagated() {
        progressProvider.addCallback(listener)

        wrappedProgressProvider.onTransitionStarted()
        fakeHandler.dispatchQueuedMessages()

        listener.assertStarted()
    }

    @Test
    fun onTransitionProgress_propagated() {
        progressProvider.addCallback(listener)

        wrappedProgressProvider.onTransitionStarted()
        wrappedProgressProvider.onTransitionProgress(0.5f)
        fakeHandler.dispatchQueuedMessages()

        listener.assertLastProgress(0.5f)
    }

    @Test
    fun onTransitionFinished_propagated() {
        progressProvider.addCallback(listener)

        wrappedProgressProvider.onTransitionStarted()
        wrappedProgressProvider.onTransitionProgress(0.5f)
        wrappedProgressProvider.onTransitionFinished()
        fakeHandler.dispatchQueuedMessages()

        listener.ensureTransitionFinished()
    }

    @Test
    fun onTransitionFinishing_propagated() {
        progressProvider.addCallback(listener)

        wrappedProgressProvider.onTransitionStarted()
        wrappedProgressProvider.onTransitionProgress(0.5f)
        wrappedProgressProvider.onTransitionFinished()
        fakeHandler.dispatchQueuedMessages()

        listener.ensureTransitionFinished()
    }

    @Test
    fun onTransitionStarted_afterCallbackRemoved_notPropagated() {
        progressProvider.addCallback(listener)
        progressProvider.removeCallback(listener)

        wrappedProgressProvider.onTransitionStarted()
        fakeHandler.dispatchQueuedMessages()

        listener.assertNotStarted()
    }
}
+42 −10
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package com.android.systemui.unfold
import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.MainThreadUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
@@ -36,15 +38,18 @@ import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManagerImpl
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import java.util.Optional
import javax.inject.Provider
import javax.inject.Singleton
import kotlin.jvm.optionals.getOrDefault

@Module(
    includes =
        [
            UnfoldFlagsModule::class,
            UnfoldSharedInternalModule::class,
            UnfoldRotationProviderInternalModule::class,
            HingeAngleProviderInternalModule::class,
@@ -69,6 +74,16 @@ class UnfoldSharedModule {
    fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
}

@Module
abstract class UnfoldFlagsModule {
    /**
     * Users of the library can bind this boolean to notify whether the progress should be
     * calculated only in the background (and the main thread provider is generated by posting the
     * background events in the main handler).
     */
    @BindsOptionalOf @UnfoldBgProgressFlag abstract fun unfoldBgProgressFlag(): Boolean
}

/**
 * Needed as methods inside must be public, but their parameters can be internal (and, a public
 * method can't have internal parameters). Making the module internal and included in a public one
@@ -87,7 +102,23 @@ internal class UnfoldSharedInternalModule {
        fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
        foldStateProvider: FoldStateProvider,
        @UnfoldMain mainHandler: Handler,
        mainThreadUnfoldTransitionProgressProviderFactory:
            MainThreadUnfoldTransitionProgressProvider.Factory,
        @UnfoldBg bgProvider: Provider<Optional<UnfoldTransitionProgressProvider>>,
        @UnfoldBgProgressFlag unfoldBgProgressFlag: Optional<Boolean>,
    ): Optional<UnfoldTransitionProgressProvider> {
        if (unfoldBgProgressFlag.getOrDefault(false)) {
            // In this case, we wrap the background progress provider
            val mainThreadProvider: Optional<UnfoldTransitionProgressProvider> =
                bgProvider.get().map {
                    mainThreadUnfoldTransitionProgressProviderFactory.create(it)
                }
            mainThreadProvider.ifPresent {
                it.addCallback(tracingListener.create("MainThreadFromBgProgress"))
            }
            return mainThreadProvider
        } else {
            // TODO(b/277879146): Remove this once unfold_animation_background_progress is launched.
            return createOptionalUnfoldTransitionProgressProvider(
                config = config,
                scaleAwareProviderFactory = scaleAwareProviderFactory,
@@ -99,6 +130,7 @@ internal class UnfoldSharedInternalModule {
                progressHandler = mainHandler,
            )
        }
    }

    @Provides
    @Singleton
+26 −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.dagger

import javax.inject.Qualifier

/**
 * Annotates the boolean representing whether we are calculating progresses in the background.
 *
 * Used to allow clients to provide this value, without depending on the flags directly.
 */
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldBgProgressFlag
+92 −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.progress

import android.os.Handler
import androidx.annotation.FloatRange
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.dagger.UnfoldMain
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

/**
 * [UnfoldTransitionProgressProvider] that forwards all progress to the main thread handler.
 *
 * This is needed when progress are calculated in the background, but some listeners need the
 * callbacks in the main thread.
 */
class MainThreadUnfoldTransitionProgressProvider
@AssistedInject
constructor(
    @UnfoldMain private val mainHandler: Handler,
    @Assisted private val rootProvider: UnfoldTransitionProgressProvider
) : UnfoldTransitionProgressProvider {

    private val listenerMap = mutableMapOf<TransitionProgressListener, TransitionProgressListener>()

    override fun addCallback(listener: TransitionProgressListener) {
        assertMainThread()
        val proxy = TransitionProgressListerProxy(listener)
        rootProvider.addCallback(proxy)
        listenerMap[listener] = proxy
    }

    override fun removeCallback(listener: TransitionProgressListener) {
        assertMainThread()
        val proxy = listenerMap.remove(listener) ?: return
        rootProvider.removeCallback(proxy)
    }

    private fun assertMainThread() {
        check(mainHandler.looper.isCurrentThread) {
            "Should be called from the main thread, but this is ${Thread.currentThread()}"
        }
    }

    override fun destroy() {
        rootProvider.destroy()
    }

    inner class TransitionProgressListerProxy(private val listener: TransitionProgressListener) :
        TransitionProgressListener {
        override fun onTransitionStarted() {
            mainHandler.post { listener.onTransitionStarted() }
        }

        override fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
            mainHandler.post { listener.onTransitionProgress(progress) }
        }

        override fun onTransitionFinishing() {
            mainHandler.post { listener.onTransitionFinishing() }
        }

        override fun onTransitionFinished() {
            mainHandler.post { listener.onTransitionFinished() }
        }
    }

    @AssistedFactory
    interface Factory {
        /** Creates a [MainThreadUnfoldTransitionProgressProvider] that wraps the [rootProvider]. */
        fun create(
            rootProvider: UnfoldTransitionProgressProvider
        ): MainThreadUnfoldTransitionProgressProvider
    }
}