Merge "Drive Launcher unfold animation from System UI" into tm-qpr-dev am: a8719c31a8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21298222

Change-Id: I831a3d8dddbdc4451e53747c7f935840ad3ef4c5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Nicolò Mazzucato
2023-02-11 10:11:36 +00:00
committed by Automerger Merge Worker
14 changed files with 556 additions and 110 deletions

View File

@@ -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";

View File

@@ -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<UnfoldTransitionProgressProvider> getUnfoldTransitionProgressProvider();
/**
* Creates a UnfoldTransitionProgressForwarder.
*/
@SysUISingleton
Optional<UnfoldTransitionProgressForwarder> getUnfoldTransitionProgressForwarder();
/**
* Creates a FoldStateLoggingProvider.
*/

View File

@@ -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<OverviewProxyLis
private final CommandQueue mCommandQueue;
private final UserTracker mUserTracker;
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder;
private final UiEventLogger mUiEventLogger;
private final DisplayTracker mDisplayTracker;
@@ -415,6 +418,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER,
mSysuiUnlockAnimationController.asBinder());
mUnfoldTransitionProgressForwarder.ifPresent(
unfoldProgressForwarder -> 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<OverviewProxyLis
DisplayTracker displayTracker,
KeyguardUnlockAnimationController sysuiUnlockAnimationController,
AssistUtils assistUtils,
DumpManager dumpManager) {
DumpManager dumpManager,
Optional<UnfoldTransitionProgressForwarder> 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<OverviewProxyLis
mSysUiState.addCallback(this::notifySystemUiStateFlags);
mUiEventLogger = uiEventLogger;
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
dumpManager.registerDumpable(getClass().getSimpleName(), this);

View File

@@ -20,17 +20,13 @@ import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.util.TestFoldStateProvider
import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,9 +41,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@Before
fun setUp() {
progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(
foldStateProvider
)
progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
progressProvider.addCallback(listener)
}
@@ -79,9 +73,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) },
)
with(listener.ensureTransitionFinished()) {
assertHasSingleFinishingEvent()
}
with(listener.ensureTransitionFinished()) { assertHasSingleFinishingEvent() }
}
@Test
@@ -150,106 +142,12 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) },
)
with(listener.ensureTransitionFinished()) {
assertHasFoldAnimationAtTheEnd()
}
}
private class TestUnfoldProgressListener : TransitionProgressListener {
private val recordings: MutableList<UnfoldTransitionRecording> = 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<Float> = 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<Float>())
}
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<Float>())
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)
}
}

View File

@@ -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()
}
}

View File

@@ -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<UnfoldTransitionRecording> = 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<Float> = 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<Float>())
}
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<Float>())
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
}
}

View File

@@ -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<RemoteUnfoldTransitionReceiver>,
): Optional<RemoteUnfoldTransitionReceiver> {
if (!config.isEnabled) {
return Optional.empty()
}
val remoteReceiver = remoteReceiverProvider.get()
remoteReceiver.addCallback(traceListener)
return Optional.of(remoteReceiver)
}
}

View File

@@ -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<UnfoldTransitionProgressProvider>
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<RemoteUnfoldTransitionReceiver>
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
)

View File

@@ -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<UnfoldTransitionProgressForwarder>
): Optional<UnfoldTransitionProgressForwarder> {
if (!config.isEnabled) {
return Optional.empty()
}
return Optional.of(progressForwarder.get())
}
}

View File

@@ -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,
)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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<TransitionProgressListener> = 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()
}
}

View File

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