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
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user