Loading packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt 0 → 100644 +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() } } packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt +42 −10 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -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 Loading @@ -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, Loading @@ -99,6 +130,7 @@ internal class UnfoldSharedInternalModule { progressHandler = mainHandler, ) } } @Provides @Singleton Loading packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt 0 → 100644 +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 packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt 0 → 100644 +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 } } Loading
packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading
packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt 0 → 100644 +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() } }
packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt +42 −10 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -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 Loading @@ -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, Loading @@ -99,6 +130,7 @@ internal class UnfoldSharedInternalModule { progressHandler = mainHandler, ) } } @Provides @Singleton Loading
packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt 0 → 100644 +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
packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt 0 → 100644 +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 } }