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 + } +}