Loading aconfig/launcher.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -133,3 +133,10 @@ flag { description: "Enables asnc inflation of workspace icons" bug: "318539160" } flag { name: "enable_unfold_state_animation" namespace: "launcher" description: "Tie unfold animation with state animation" bug: "297057373" } quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +7 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.launcher3.BaseActivity.EVENT_DESTROYED; import static com.android.launcher3.Flags.enableUnfoldStateAnimation; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate; Loading Loading @@ -405,8 +406,12 @@ public class TaskbarManager { */ private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity( StatefulActivity activity) { if (activity instanceof QuickstepLauncher) { return ((QuickstepLauncher) activity).getUnfoldTransitionProgressProvider(); if (!enableUnfoldStateAnimation()) { if (activity instanceof QuickstepLauncher ql) { return ql.getUnfoldTransitionProgressProvider(); } } else { return SystemUiProxy.INSTANCE.get(mContext).getUnfoldTransitionProvider(); } return null; } Loading quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +14 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEAS import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.launcher3.Flags.enableUnfoldStateAnimation; import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO; import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE; import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON; Loading Loading @@ -170,6 +171,8 @@ import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SplitToWorkspaceController; import com.android.quickstep.util.SplitWithKeyboardShortcutController; import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.util.unfold.LauncherUnfoldTransitionController; import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider; import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; Loading Loading @@ -965,10 +968,18 @@ public class QuickstepLauncher extends Launcher { } private void initUnfoldTransitionProgressProvider() { if (!enableUnfoldStateAnimation()) { final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig(); if (config.isEnabled()) { initRemotelyCalculatedUnfoldAnimation(config); } } else { ProxyUnfoldTransitionProvider provider = SystemUiProxy.INSTANCE.get(this).getUnfoldTransitionProvider(); if (provider != null) { new LauncherUnfoldTransitionController(this, provider); } } } /** Receives animation progress from sysui process. */ Loading quickstep/src/com/android/quickstep/SystemUiProxy.java +30 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.quickstep; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static com.android.launcher3.Flags.enableUnfoldStateAnimation; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition; Loading Loading @@ -67,6 +68,7 @@ import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistUtils; import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; Loading @@ -74,6 +76,7 @@ import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController; import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; import com.android.systemui.shared.system.smartspace.SmartspaceState; import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig; import com.android.systemui.unfold.progress.IUnfoldAnimation; import com.android.systemui.unfold.progress.IUnfoldTransitionListener; import com.android.wm.shell.back.IBackAnimation; Loading Loading @@ -177,7 +180,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { */ private final PendingIntent mRecentsPendingIntent; public SystemUiProxy(Context context) { @Nullable private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider; private SystemUiProxy(Context context) { mContext = context; mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync); final Intent baseIntent = new Intent().setPackage(mContext.getPackageName()); Loading @@ -187,6 +193,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT | Intent.FILL_IN_COMPONENT, options.toBundle()); mUnfoldTransitionProvider = (enableUnfoldStateAnimation() && new ResourceUnfoldTransitionConfig().isEnabled()) ? new ProxyUnfoldTransitionProvider() : null; } @Override Loading Loading @@ -251,7 +261,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { mRecentTasks = recentTasks; mBackAnimation = backAnimation; mDesktopMode = desktopMode; mUnfoldAnimation = unfoldAnimation; mUnfoldAnimation = enableUnfoldStateAnimation() ? null : unfoldAnimation; mDragAndDrop = dragAndDrop; linkToDeath(); // re-attach the listeners once missing due to setProxy has not been initialized yet. Loading @@ -272,6 +282,19 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { setAssistantOverridesRequested( AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes()); mStateChangeCallbacks.forEach(Runnable::run); if (mUnfoldTransitionProvider != null) { if (unfoldAnimation != null) { try { unfoldAnimation.setListener(mUnfoldTransitionProvider); mUnfoldTransitionProvider.setActive(true); } catch (RemoteException e) { // Ignore } } else { mUnfoldTransitionProvider.setActive(false); } } } /** Loading Loading @@ -1451,6 +1474,11 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { } } @Nullable public ProxyUnfoldTransitionProvider getUnfoldTransitionProvider() { return mUnfoldTransitionProvider; } // // Recents // Loading quickstep/src/com/android/quickstep/util/unfold/LauncherUnfoldTransitionController.kt 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.quickstep.util.unfold import android.app.Activity import android.os.Trace import android.view.Surface import com.android.launcher3.Alarm import com.android.launcher3.DeviceProfile import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener import com.android.launcher3.Launcher import com.android.launcher3.anim.PendingAnimation import com.android.launcher3.config.FeatureFlags import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener /** Controls animations that are happening during unfolding foldable devices */ class LauncherUnfoldTransitionController( private val launcher: Launcher, private val progressProvider: ProxyUnfoldTransitionProvider ) : OnDeviceProfileChangeListener, ActivityLifecycleCallbacksAdapter, TransitionProgressListener { private var isTablet: Boolean? = null private var hasUnfoldTransitionStarted = false private val timeoutAlarm = Alarm().apply { setOnAlarmListener { onTransitionFinished() Trace.endAsyncSection("$TAG#startedPreemptively", 0) } } init { launcher.addOnDeviceProfileChangeListener(this) launcher.registerActivityLifecycleCallbacks(this) } override fun onActivityPaused(activity: Activity) { progressProvider.removeCallback(this) } override fun onActivityResumed(activity: Activity) { progressProvider.addCallback(this) } override fun onDeviceProfileChanged(dp: DeviceProfile) { if (!FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) { return } if (isTablet != null && dp.isTablet != isTablet) { // We should preemptively start the animation only if: // - We changed to the unfolded screen // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't // receive transition progress events from SystemUI later because there was no // IPC connection established (e.g. because of SystemUI crash) // - SystemUI has not already sent unfold animation progress events. This might happen // if Launcher was not open during unfold, in this case we receive the configuration // change only after we went back to home screen and we don't want to start the // animation in this case. if (dp.isTablet && progressProvider.isActive && !hasUnfoldTransitionStarted) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked onTransitionStarted() Trace.beginAsyncSection("$TAG#startedPreemptively", 0) timeoutAlarm.setAlarm(PREEMPTIVE_UNFOLD_TIMEOUT_MS) } if (!dp.isTablet) { // Reset unfold transition status when folded hasUnfoldTransitionStarted = false } } isTablet = dp.isTablet } override fun onTransitionStarted() { hasUnfoldTransitionStarted = true launcher.animationCoordinator.setAnimation( provider = this, factory = this::onPrepareUnfoldAnimation, duration = 1000L // The expected duration for the animation. Then only comes to play if we have // to run the animation ourselves in case sysui misses the end signal ) timeoutAlarm.cancelAlarm() } override fun onTransitionProgress(progress: Float) { hasUnfoldTransitionStarted = true launcher.animationCoordinator.getPlaybackController(this)?.setPlayFraction(progress) } override fun onTransitionFinished() { // Run the animation to end the animation in case it is not already at end progress. It // will scale the duration to the remaining progress launcher.animationCoordinator.getPlaybackController(this)?.start() timeoutAlarm.cancelAlarm() } private fun onPrepareUnfoldAnimation(anim: PendingAnimation) { val dp = launcher.deviceProfile val rotation = dp.displayInfo.rotation val isVertical = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 UnfoldAnimationBuilder.buildUnfoldAnimation( launcher, isVertical, dp.displayInfo.currentSize, anim ) } companion object { private const val TAG = "LauncherUnfoldTransitionController" } } Loading
aconfig/launcher.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -133,3 +133,10 @@ flag { description: "Enables asnc inflation of workspace icons" bug: "318539160" } flag { name: "enable_unfold_state_animation" namespace: "launcher" description: "Tie unfold animation with state animation" bug: "297057373" }
quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +7 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.launcher3.BaseActivity.EVENT_DESTROYED; import static com.android.launcher3.Flags.enableUnfoldStateAnimation; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate; Loading Loading @@ -405,8 +406,12 @@ public class TaskbarManager { */ private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity( StatefulActivity activity) { if (activity instanceof QuickstepLauncher) { return ((QuickstepLauncher) activity).getUnfoldTransitionProgressProvider(); if (!enableUnfoldStateAnimation()) { if (activity instanceof QuickstepLauncher ql) { return ql.getUnfoldTransitionProgressProvider(); } } else { return SystemUiProxy.INSTANCE.get(mContext).getUnfoldTransitionProvider(); } return null; } Loading
quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +14 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEAS import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; import static com.android.app.animation.Interpolators.EMPHASIZED; import static com.android.launcher3.Flags.enableUnfoldStateAnimation; import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO; import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE; import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON; Loading Loading @@ -170,6 +171,8 @@ import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SplitToWorkspaceController; import com.android.quickstep.util.SplitWithKeyboardShortcutController; import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.util.unfold.LauncherUnfoldTransitionController; import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider; import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; Loading Loading @@ -965,10 +968,18 @@ public class QuickstepLauncher extends Launcher { } private void initUnfoldTransitionProgressProvider() { if (!enableUnfoldStateAnimation()) { final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig(); if (config.isEnabled()) { initRemotelyCalculatedUnfoldAnimation(config); } } else { ProxyUnfoldTransitionProvider provider = SystemUiProxy.INSTANCE.get(this).getUnfoldTransitionProvider(); if (provider != null) { new LauncherUnfoldTransitionController(this, provider); } } } /** Receives animation progress from sysui process. */ Loading
quickstep/src/com/android/quickstep/SystemUiProxy.java +30 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.quickstep; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static com.android.launcher3.Flags.enableUnfoldStateAnimation; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition; Loading Loading @@ -67,6 +68,7 @@ import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistUtils; import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; Loading @@ -74,6 +76,7 @@ import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController; import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; import com.android.systemui.shared.system.smartspace.SmartspaceState; import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig; import com.android.systemui.unfold.progress.IUnfoldAnimation; import com.android.systemui.unfold.progress.IUnfoldTransitionListener; import com.android.wm.shell.back.IBackAnimation; Loading Loading @@ -177,7 +180,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { */ private final PendingIntent mRecentsPendingIntent; public SystemUiProxy(Context context) { @Nullable private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider; private SystemUiProxy(Context context) { mContext = context; mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync); final Intent baseIntent = new Intent().setPackage(mContext.getPackageName()); Loading @@ -187,6 +193,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT | Intent.FILL_IN_COMPONENT, options.toBundle()); mUnfoldTransitionProvider = (enableUnfoldStateAnimation() && new ResourceUnfoldTransitionConfig().isEnabled()) ? new ProxyUnfoldTransitionProvider() : null; } @Override Loading Loading @@ -251,7 +261,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { mRecentTasks = recentTasks; mBackAnimation = backAnimation; mDesktopMode = desktopMode; mUnfoldAnimation = unfoldAnimation; mUnfoldAnimation = enableUnfoldStateAnimation() ? null : unfoldAnimation; mDragAndDrop = dragAndDrop; linkToDeath(); // re-attach the listeners once missing due to setProxy has not been initialized yet. Loading @@ -272,6 +282,19 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { setAssistantOverridesRequested( AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes()); mStateChangeCallbacks.forEach(Runnable::run); if (mUnfoldTransitionProvider != null) { if (unfoldAnimation != null) { try { unfoldAnimation.setListener(mUnfoldTransitionProvider); mUnfoldTransitionProvider.setActive(true); } catch (RemoteException e) { // Ignore } } else { mUnfoldTransitionProvider.setActive(false); } } } /** Loading Loading @@ -1451,6 +1474,11 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { } } @Nullable public ProxyUnfoldTransitionProvider getUnfoldTransitionProvider() { return mUnfoldTransitionProvider; } // // Recents // Loading
quickstep/src/com/android/quickstep/util/unfold/LauncherUnfoldTransitionController.kt 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.quickstep.util.unfold import android.app.Activity import android.os.Trace import android.view.Surface import com.android.launcher3.Alarm import com.android.launcher3.DeviceProfile import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener import com.android.launcher3.Launcher import com.android.launcher3.anim.PendingAnimation import com.android.launcher3.config.FeatureFlags import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener /** Controls animations that are happening during unfolding foldable devices */ class LauncherUnfoldTransitionController( private val launcher: Launcher, private val progressProvider: ProxyUnfoldTransitionProvider ) : OnDeviceProfileChangeListener, ActivityLifecycleCallbacksAdapter, TransitionProgressListener { private var isTablet: Boolean? = null private var hasUnfoldTransitionStarted = false private val timeoutAlarm = Alarm().apply { setOnAlarmListener { onTransitionFinished() Trace.endAsyncSection("$TAG#startedPreemptively", 0) } } init { launcher.addOnDeviceProfileChangeListener(this) launcher.registerActivityLifecycleCallbacks(this) } override fun onActivityPaused(activity: Activity) { progressProvider.removeCallback(this) } override fun onActivityResumed(activity: Activity) { progressProvider.addCallback(this) } override fun onDeviceProfileChanged(dp: DeviceProfile) { if (!FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) { return } if (isTablet != null && dp.isTablet != isTablet) { // We should preemptively start the animation only if: // - We changed to the unfolded screen // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't // receive transition progress events from SystemUI later because there was no // IPC connection established (e.g. because of SystemUI crash) // - SystemUI has not already sent unfold animation progress events. This might happen // if Launcher was not open during unfold, in this case we receive the configuration // change only after we went back to home screen and we don't want to start the // animation in this case. if (dp.isTablet && progressProvider.isActive && !hasUnfoldTransitionStarted) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked onTransitionStarted() Trace.beginAsyncSection("$TAG#startedPreemptively", 0) timeoutAlarm.setAlarm(PREEMPTIVE_UNFOLD_TIMEOUT_MS) } if (!dp.isTablet) { // Reset unfold transition status when folded hasUnfoldTransitionStarted = false } } isTablet = dp.isTablet } override fun onTransitionStarted() { hasUnfoldTransitionStarted = true launcher.animationCoordinator.setAnimation( provider = this, factory = this::onPrepareUnfoldAnimation, duration = 1000L // The expected duration for the animation. Then only comes to play if we have // to run the animation ourselves in case sysui misses the end signal ) timeoutAlarm.cancelAlarm() } override fun onTransitionProgress(progress: Float) { hasUnfoldTransitionStarted = true launcher.animationCoordinator.getPlaybackController(this)?.setPlayFraction(progress) } override fun onTransitionFinished() { // Run the animation to end the animation in case it is not already at end progress. It // will scale the duration to the remaining progress launcher.animationCoordinator.getPlaybackController(this)?.start() timeoutAlarm.cancelAlarm() } private fun onPrepareUnfoldAnimation(anim: PendingAnimation) { val dp = launcher.deviceProfile val rotation = dp.displayInfo.rotation val isVertical = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 UnfoldAnimationBuilder.buildUnfoldAnimation( launcher, isVertical, dp.displayInfo.currentSize, anim ) } companion object { private const val TAG = "LauncherUnfoldTransitionController" } }