From 5903ebdc0d6d396623543a1116d71f5815462626 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Mon, 6 Feb 2023 18:38:50 +0000 Subject: [PATCH] Drive Launcher unfold animation from System UI Before this cl, both Launcher and System UI processes were registering for hinge angle and device state (e.g. folded/unfolded) change events to calculate the current unfold animation progress. In some cases, launcher ui thread was busy, delaying the progress calculation from the hinge angle value (that was received ~at the same time by both processes). This resulted in launcher and sysui unfold animation not being synchronized in some cases. With this cl, System UI process uses OverviewProxyService to send the unfold events to Launcher. In this way, both process always have the exact same progress (+- 1 frame) This is currently guarded by a launcher flag, by default with the new behaviour, to allow devs to compare the experience and easily debug potential regressions. Bug: 268490854 Test: Analysed perfetto trace + RemoteUnfoldTransitionProgressProviderTest + manual Change-Id: Icdf3932644a545f4dbe077c8284911bf99f3d81c --- .../shared/system/QuickStepContract.java | 1 + .../systemui/dagger/SysUIComponent.java | 18 +++ .../recents/OverviewProxyService.java | 12 +- ...sedUnfoldTransitionProgressProviderTest.kt | 116 +-------------- .../RemoteUnfoldTransitionReceiverTest.kt | 72 ++++++++++ .../progress/TestUnfoldProgressListener.kt | 136 ++++++++++++++++++ .../systemui/unfold/UnfoldRemoteModule.kt | 45 ++++++ .../systemui/unfold/UnfoldSharedComponent.kt | 36 +++++ .../systemui/unfold/UnfoldSharedModule.kt | 13 ++ .../unfold/UnfoldTransitionFactory.kt | 23 +++ .../unfold/progress/IUnfoldAnimation.aidl | 34 +++++ .../progress/IUnfoldTransitionListener.aidl | 38 +++++ .../RemoteUnfoldTransitionReceiver.kt | 61 ++++++++ .../UnfoldTransitionProgressForwarder.kt | 61 ++++++++ 14 files changed, 556 insertions(+), 110 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt create mode 100644 packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt create mode 100644 packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl create mode 100644 packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl create mode 100644 packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt create mode 100644 packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 037a71ea3eac2..65dedc6eaf4b8 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -43,6 +43,7 @@ public class QuickStepContract { public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; + public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation"; // See ISysuiUnlockAnimationController.aidl public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 68f4dbe535007..625a028013927 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -35,6 +35,8 @@ import com.android.systemui.unfold.FoldStateLogger; import com.android.systemui.unfold.FoldStateLoggingProvider; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldLatencyTracker; +import com.android.systemui.unfold.UnfoldTransitionProgressProvider; +import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder; import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; @@ -137,6 +139,10 @@ public interface SysUIComponent { getUnfoldLatencyTracker().init(); getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init); getFoldStateLogger().ifPresent(FoldStateLogger::init); + getUnfoldTransitionProgressProvider().ifPresent((progressProvider) -> + getUnfoldTransitionProgressForwarder().ifPresent((forwarder) -> + progressProvider.addCallback(forwarder) + )); } /** @@ -163,6 +169,18 @@ public interface SysUIComponent { @SysUISingleton UnfoldLatencyTracker getUnfoldLatencyTracker(); + /** + * Creates a UnfoldTransitionProgressProvider. + */ + @SysUISingleton + Optional getUnfoldTransitionProgressProvider(); + + /** + * Creates a UnfoldTransitionProgressForwarder. + */ + @SysUISingleton + Optional getUnfoldTransitionProgressForwarder(); + /** * Creates a FoldStateLoggingProvider. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index a979e5a99fa8b..25ff308b46bbb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,6 +25,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; @@ -99,6 +100,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; +import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder; import com.android.wm.shell.sysui.ShellInterface; import java.io.PrintWriter; @@ -144,6 +146,7 @@ public class OverviewProxyService implements CallbackController mUnfoldTransitionProgressForwarder; private final UiEventLogger mUiEventLogger; private final DisplayTracker mDisplayTracker; @@ -415,6 +418,10 @@ public class OverviewProxyService implements CallbackController params.putBinder( + KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER, + unfoldProgressForwarder.asBinder())); // Add all the interfaces exposed by the shell mShellInterface.createExternalInterfaces(params); @@ -512,7 +519,9 @@ public class OverviewProxyService implements CallbackController unfoldTransitionProgressForwarder + ) { // b/241601880: This component shouldn't be running for a non-primary user if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) { Log.e(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable()); @@ -538,6 +547,7 @@ public class OverviewProxyService implements CallbackController = arrayListOf() - private var currentRecording: UnfoldTransitionRecording? = null - - override fun onTransitionStarted() { - assertWithMessage("Trying to start a transition when it is already in progress") - .that(currentRecording).isNull() - - currentRecording = UnfoldTransitionRecording() - } - - override fun onTransitionProgress(progress: Float) { - assertWithMessage("Received transition progress event when it's not started") - .that(currentRecording).isNotNull() - currentRecording!!.addProgress(progress) - } - - override fun onTransitionFinishing() { - assertWithMessage("Received transition finishing event when it's not started") - .that(currentRecording).isNotNull() - currentRecording!!.onFinishing() - } - - override fun onTransitionFinished() { - assertWithMessage("Received transition finish event when it's not started") - .that(currentRecording).isNotNull() - recordings += currentRecording!! - currentRecording = null - } - - fun ensureTransitionFinished(): UnfoldTransitionRecording { - waitForCondition { recordings.size == 1 } - return recordings.first() - } - - class UnfoldTransitionRecording { - private val progressHistory: MutableList = arrayListOf() - private var finishingInvocations: Int = 0 - - fun addProgress(progress: Float) { - assertThat(progress).isAtMost(1.0f) - assertThat(progress).isAtLeast(0.0f) - - progressHistory += progress - } - - fun onFinishing() { - finishingInvocations++ - } - - fun assertIncreasingProgress() { - assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS) - assertThat(progressHistory).isInOrder() - } - - fun assertDecreasingProgress() { - assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS) - assertThat(progressHistory).isInOrder(Comparator.reverseOrder()) - } - - fun assertFinishedWithUnfold() { - assertThat(progressHistory).isNotEmpty() - assertThat(progressHistory.last()).isEqualTo(1.0f) - } - - fun assertFinishedWithFold() { - assertThat(progressHistory).isNotEmpty() - assertThat(progressHistory.last()).isEqualTo(0.0f) - } - - fun assertHasFoldAnimationAtTheEnd() { - // Check that there are at least a few decreasing events at the end - assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS) - assertThat(progressHistory.takeLast(MIN_ANIMATION_EVENTS)) - .isInOrder(Comparator.reverseOrder()) - assertThat(progressHistory.last()).isEqualTo(0.0f) - } - - fun assertHasSingleFinishingEvent() { - assertWithMessage("onTransitionFinishing callback should be invoked exactly " + - "one time").that(finishingInvocations).isEqualTo(1) - } - } - - private companion object { - private const val MIN_ANIMATION_EVENTS = 5 - } + with(listener.ensureTransitionFinished()) { assertHasFoldAnimationAtTheEnd() } } private fun runOnMainThreadWithInterval(vararg blocks: () -> Unit, intervalMillis: Long = 60) { blocks.forEach { - InstrumentationRegistry.getInstrumentation().runOnMainSync { - it() - } + InstrumentationRegistry.getInstrumentation().runOnMainSync { it() } Thread.sleep(intervalMillis) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt new file mode 100644 index 0000000000000..0e7e039e69e2e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt @@ -0,0 +1,72 @@ +/* + * 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.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class RemoteUnfoldTransitionReceiverTest : SysuiTestCase() { + + private val progressProvider = RemoteUnfoldTransitionReceiver { it.run() } + private val listener = TestUnfoldProgressListener() + + @Before + fun setUp() { + progressProvider.addCallback(listener) + } + + @Test + fun onTransitionStarted_propagated() { + progressProvider.onTransitionStarted() + + listener.assertStarted() + } + + @Test + fun onTransitionProgress_propagated() { + progressProvider.onTransitionStarted() + + progressProvider.onTransitionProgress(0.5f) + + listener.assertLastProgress(0.5f) + } + + @Test + fun onTransitionEnded_propagated() { + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + + progressProvider.onTransitionFinished() + + listener.ensureTransitionFinished() + } + + @Test + fun onTransitionStarted_afterCallbackRemoved_notPropagated() { + progressProvider.removeCallback(listener) + + progressProvider.onTransitionStarted() + + listener.assertNotStarted() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt new file mode 100644 index 0000000000000..f6532070d7202 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt @@ -0,0 +1,136 @@ +/* + * 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 com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage + +/** Listener usable by tests with some handy assertions. */ +class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionProgressListener { + + private val recordings: MutableList = arrayListOf() + private var currentRecording: UnfoldTransitionRecording? = null + + override fun onTransitionStarted() { + assertWithMessage("Trying to start a transition when it is already in progress") + .that(currentRecording) + .isNull() + + currentRecording = UnfoldTransitionRecording() + } + + override fun onTransitionProgress(progress: Float) { + assertWithMessage("Received transition progress event when it's not started") + .that(currentRecording) + .isNotNull() + currentRecording!!.addProgress(progress) + } + + override fun onTransitionFinishing() { + assertWithMessage("Received transition finishing event when it's not started") + .that(currentRecording) + .isNotNull() + currentRecording!!.onFinishing() + } + + override fun onTransitionFinished() { + assertWithMessage("Received transition finish event when it's not started") + .that(currentRecording) + .isNotNull() + recordings += currentRecording!! + currentRecording = null + } + + fun ensureTransitionFinished(): UnfoldTransitionRecording { + waitForCondition { recordings.size == 1 } + return recordings.first() + } + + fun assertStarted() { + assertWithMessage("Transition didn't start").that(currentRecording).isNotNull() + } + + fun assertNotStarted() { + assertWithMessage("Transition started").that(currentRecording).isNull() + } + + fun assertLastProgress(progress: Float) { + currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.") + } + + class UnfoldTransitionRecording { + private val progressHistory: MutableList = arrayListOf() + private var finishingInvocations: Int = 0 + + fun addProgress(progress: Float) { + assertThat(progress).isAtMost(1.0f) + assertThat(progress).isAtLeast(0.0f) + + progressHistory += progress + } + + fun onFinishing() { + finishingInvocations++ + } + + fun assertIncreasingProgress() { + assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS) + assertThat(progressHistory).isInOrder() + } + + fun assertDecreasingProgress() { + assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS) + assertThat(progressHistory).isInOrder(Comparator.reverseOrder()) + } + + fun assertFinishedWithUnfold() { + assertThat(progressHistory).isNotEmpty() + assertThat(progressHistory.last()).isEqualTo(1.0f) + } + + fun assertFinishedWithFold() { + assertThat(progressHistory).isNotEmpty() + assertThat(progressHistory.last()).isEqualTo(0.0f) + } + + fun assertHasFoldAnimationAtTheEnd() { + // Check that there are at least a few decreasing events at the end + assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS) + assertThat(progressHistory.takeLast(MIN_ANIMATION_EVENTS)) + .isInOrder(Comparator.reverseOrder()) + assertThat(progressHistory.last()).isEqualTo(0.0f) + } + + fun assertHasSingleFinishingEvent() { + assertWithMessage( + "onTransitionFinishing callback should be invoked exactly " + "one time" + ) + .that(finishingInvocations) + .isEqualTo(1) + } + + fun assertLastProgress(progress: Float) { + assertThat(progressHistory.last()).isEqualTo(progress) + } + } + + private companion object { + private const val MIN_ANIMATION_EVENTS = 5 + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt new file mode 100644 index 0000000000000..b395d9c076625 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt @@ -0,0 +1,45 @@ +/* + * 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 + +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver +import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener +import dagger.Module +import dagger.Provides +import java.util.Optional +import javax.inject.Provider +import javax.inject.Singleton + +/** Binds classes needed to provide unfold transition progresses to another process. */ +@Module +class UnfoldRemoteModule { + @Provides + @Singleton + fun provideTransitionProvider( + config: UnfoldTransitionConfig, + traceListener: ATraceLoggerTransitionProgressListener, + remoteReceiverProvider: Provider, + ): Optional { + if (!config.isEnabled) { + return Optional.empty() + } + val remoteReceiver = remoteReceiverProvider.get() + remoteReceiver.addCallback(traceListener) + return Optional.of(remoteReceiver) + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt index cfb959e51d4ef..068347cfe9d82 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt @@ -24,6 +24,7 @@ import android.view.IWindowManager import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg +import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider @@ -68,3 +69,38 @@ interface UnfoldSharedComponent { val unfoldTransitionProvider: Optional val rotationChangeProvider: RotationChangeProvider } + +/** + * Generates a [RemoteTransitionProgress] usable to receive unfold transition progress from another + * process. + */ +@Singleton +@Component(modules = [UnfoldRemoteModule::class]) +interface RemoteUnfoldSharedComponent { + + @Component.Factory + interface Factory { + fun create( + @BindsInstance context: Context, + @BindsInstance config: UnfoldTransitionConfig, + @BindsInstance @UnfoldMain executor: Executor, + @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor, + @BindsInstance windowManager: IWindowManager, + @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String, + ): RemoteUnfoldSharedComponent + } + + val remoteTransitionProgress: Optional + val rotationChangeProvider: RotationChangeProvider +} + +/** + * Usable to receive and propagate unfold transition progresses + * + * All unfold events received by [remoteReceiver] will be propagated to [localProvider]. + * [remoteReceiver] is meant to receive events from a remote process (E.g. from a binder service). + */ +data class RemoteTransitionProgress( + val localProvider: UnfoldTransitionProgressProvider, + val remoteReceiver: UnfoldTransitionProgressProvider.TransitionProgressListener +) diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt index 31616fa54bf40..5ffc094b88b3d 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.unfold 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.progress.UnfoldTransitionProgressForwarder import com.android.systemui.unfold.updates.DeviceFoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider @@ -102,4 +103,16 @@ internal class UnfoldSharedInternalModule { EmptyHingeAngleProvider } } + + @Provides + @Singleton + fun provideProgressForwarder( + config: UnfoldTransitionConfig, + progressForwarder: Provider + ): Optional { + if (!config.isEnabled) { + return Optional.empty() + } + return Optional.of(progressForwarder.get()) + } } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index aa93c62901452..8eb79df554964 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -63,3 +63,26 @@ fun createUnfoldSharedComponent( tracingTagPrefix, windowManager, ) + +/** + * Factory for [RemoteUnfoldSharedComponent]. + * + * Wraps [DaggerRemoteUnfoldSharedComponent] (that is autogenerated), for better discoverability. + */ +fun createRemoteUnfoldSharedComponent( + context: Context, + config: UnfoldTransitionConfig, + mainExecutor: Executor, + singleThreadBgExecutor: Executor, + tracingTagPrefix: String, + windowManager: IWindowManager, + ): RemoteUnfoldSharedComponent = + DaggerRemoteUnfoldSharedComponent.factory() + .create( + context, + config, + mainExecutor, + singleThreadBgExecutor, + windowManager, + tracingTagPrefix, + ) diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl new file mode 100644 index 0000000000000..07a1db4183ae3 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl @@ -0,0 +1,34 @@ +/* + * 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 com.android.systemui.unfold.progress.IUnfoldTransitionListener; + + +/** + * Interface exposed by System UI to allow remote process to register for unfold animation events. + */ +oneway interface IUnfoldAnimation { + + /** + * Sets a listener for the animation. + * + * Only one listener is supported. If there are multiple, the earlier one will be overridden. + */ + void setListener(in IUnfoldTransitionListener listener); +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl new file mode 100644 index 0000000000000..8f46b1b4a4be7 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl @@ -0,0 +1,38 @@ +/* + * 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; + + +/** + * Implemented by remote processes to receive unfold animation events from System UI. + */ +oneway interface IUnfoldTransitionListener { + /** + * Sent when unfold animation started. + */ + void onTransitionStarted() = 1; + + /** + * Sent when unfold animation progress changes. + */ + void onTransitionProgress(float progress) = 2; + + /** + * Sent when unfold animation finished. + */ + void onTransitionFinished() = 3; +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt new file mode 100644 index 0000000000000..5e4bcc97520e2 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt @@ -0,0 +1,61 @@ +/* + * 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 com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.dagger.UnfoldMain +import java.util.concurrent.Executor +import javax.inject.Inject + +/** + * Receives unfold events from remote senders (System UI). + * + * A binder to an instance to this class (created with [RemoteUnfoldTransitionReceiver.asBinder]) + * should be sent to the remote process providing events. + */ +class RemoteUnfoldTransitionReceiver +@Inject +constructor(@UnfoldMain private val executor: Executor) : + UnfoldTransitionProgressProvider, IUnfoldTransitionListener.Stub() { + + private val listeners: MutableSet = mutableSetOf() + + override fun onTransitionStarted() { + executor.execute { listeners.forEach { it.onTransitionStarted() } } + } + + override fun onTransitionProgress(progress: Float) { + executor.execute { listeners.forEach { it.onTransitionProgress(progress) } } + } + + override fun onTransitionFinished() { + executor.execute { listeners.forEach { it.onTransitionFinished() } } + } + + override fun addCallback(listener: TransitionProgressListener) { + listeners += listener + } + + override fun removeCallback(listener: TransitionProgressListener) { + listeners -= listener + } + + override fun destroy() { + listeners.clear() + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt new file mode 100644 index 0000000000000..b6545215690de --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt @@ -0,0 +1,61 @@ +/* + * 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.RemoteException +import android.util.Log +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import javax.inject.Inject + +/** Forwards received unfold events to [remoteListener], when present. */ +class UnfoldTransitionProgressForwarder @Inject constructor() : + TransitionProgressListener, IUnfoldAnimation.Stub() { + + private var remoteListener: IUnfoldTransitionListener? = null + + override fun onTransitionStarted() { + try { + Log.d(TAG, "onTransitionStarted") + remoteListener?.onTransitionStarted() + } catch (e: RemoteException) { + Log.e(TAG, "Failed call onTransitionStarted", e) + } + } + + override fun onTransitionFinished() { + try { + Log.d(TAG, "onTransitionFinished") + remoteListener?.onTransitionFinished() + } catch (e: RemoteException) { + Log.e(TAG, "Failed call onTransitionFinished", e) + } + } + + override fun onTransitionProgress(progress: Float) { + try { + remoteListener?.onTransitionProgress(progress) + } catch (e: RemoteException) { + Log.e(TAG, "Failed call onTransitionProgress", e) + } + } + + override fun setListener(listener: IUnfoldTransitionListener?) { + remoteListener = listener + } + + companion object { + private val TAG = UnfoldTransitionProgressForwarder::class.java.simpleName + } +}