diff --git a/Android.bp b/Android.bp index f5a38b4e6f..c980a2e50f 100644 --- a/Android.bp +++ b/Android.bp @@ -31,6 +31,7 @@ android_library { "androidx.test.uiautomator_uiautomator", "androidx.preference_preference", "SystemUISharedLib", + "SystemUIAnimationLib", ], srcs: [ "tests/tapl/**/*.java", @@ -196,6 +197,7 @@ android_library { "lottie", "SystemUISharedLib", "SystemUI-statsd", + "SystemUIAnimationLib", ], manifest: "quickstep/AndroidManifest.xml", min_sdk_version: "current", @@ -287,6 +289,7 @@ android_library { "SystemUISharedLib", "Launcher3CommonDepsLib", "QuickstepResLib", + "SystemUIAnimationLib", ], manifest: "quickstep/AndroidManifest.xml", platform_apis: true, diff --git a/OWNERS b/OWNERS index 05fa502b34..7f98ea6a78 100644 --- a/OWNERS +++ b/OWNERS @@ -39,6 +39,7 @@ peanutbutter@google.com xuqiu@google.com sreyasr@google.com thiruram@google.com +brianji@google.com per-file FeatureFlags.java, globs = set noparent per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index d4969834fc..6f0f993ed4 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -292,8 +292,8 @@ public abstract class BaseQuickstepLauncher extends Launcher { mActionsView = findViewById(R.id.overview_actions_view); RecentsView overviewPanel = (RecentsView) getOverviewPanel(); SplitSelectStateController controller = - new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this), - getStateManager(), getDepthController()); + new SplitSelectStateController(this, mHandler, getStateManager(), + getDepthController()); overviewPanel.init(mActionsView, controller); mActionsView.setDp(getDeviceProfile()); mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this)); diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 8f8ac8e415..e5910df9bd 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1301,8 +1301,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - return mLauncher.getFirstMatchForAppClose(launchCookieItemId, - packageName, UserHandle.of(runningTaskTarget.taskInfo.userId)); + return mLauncher.getFirstMatchForAppClose(launchCookieItemId, packageName, + UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */); } private @NonNull RectF getDefaultWindowTargetRect() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index ebe6a04611..235a156fb5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -29,13 +29,16 @@ import androidx.annotation.NonNull; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.views.RecentsView; +import com.android.systemui.animation.ViewRootSync; import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.HashMap; @@ -76,6 +79,9 @@ import java.util.function.Supplier; private boolean mShouldDelayLauncherStateAnim; + // We skip any view synchronizations during init/destroy. + private boolean mCanSyncViews; + private final StateManager.StateListener mStateListener = new StateManager.StateListener() { @@ -102,6 +108,8 @@ import java.util.function.Supplier; }; public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) { + mCanSyncViews = false; + mControllers = controllers; mLauncher = launcher; @@ -121,9 +129,13 @@ import java.util.function.Supplier; updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed()); mLauncherState = launcher.getStateManager().getState(); applyState(0); + + mCanSyncViews = true; } public void onDestroy() { + mCanSyncViews = false; + mIconAlignmentForResumedState.finishAnimation(); mIconAlignmentForGestureState.finishAnimation(); mIconAlignmentForLauncherState.finishAnimation(); @@ -131,6 +143,8 @@ import java.util.function.Supplier; mIconAlphaForHome.setConsumer(null); mLauncher.getHotseat().setIconsAlpha(1f); mLauncher.getStateManager().removeStateListener(mStateListener); + + mCanSyncViews = true; } public Animator createAnimToLauncher(@NonNull LauncherState toState, @@ -380,6 +394,27 @@ import java.util.function.Supplier; return; } float alignment = alignmentSupplier.get(); + float currentValue = mIconAlphaForHome.getValue(); + boolean taskbarWillBeVisible = alignment < 1; + boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0) + || (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0); + + // Sync the first frame where we swap taskbar and hotseat. + if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) { + DeviceProfile dp = mLauncher.getDeviceProfile(); + + // Do all the heavy work before the sync. + mControllers.taskbarViewController.createIconAlignmentControllerIfNotExists(dp); + + ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(), + mControllers.taskbarActivityContext.getDragLayer(), + () -> updateIconAlignment(alignment)); + } else { + updateIconAlignment(alignment); + } + } + + private void updateIconAlignment(float alignment) { mControllers.taskbarViewController.setLauncherIconAlignment( alignment, mLauncher.getDeviceProfile()); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index a89061bf11..1320060202 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -182,6 +182,16 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar + mTaskbarIconTranslationYForStash.value); } + /** + * Creates the icon alignment controller if it does not already exist. + * @param launcherDp Launcher device profile. + */ + public void createIconAlignmentControllerIfNotExists(DeviceProfile launcherDp) { + if (mIconAlignControllerLazy == null) { + mIconAlignControllerLazy = createIconAlignmentController(launcherDp); + } + } + /** * Sets the taskbar icon alignment relative to Launcher hotseat icons * @param alignmentRatio [0, 1] @@ -189,9 +199,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * 1 => fully aligned */ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { - if (mIconAlignControllerLazy == null) { - mIconAlignControllerLazy = createIconAlignmentController(launcherDp); - } + createIconAlignmentControllerIfNotExists(launcherDp); mIconAlignControllerLazy.setPlayFraction(alignmentRatio); if (alignmentRatio <= 0 || alignmentRatio >= 1) { // Cleanup lazy controller so that it is created again in next animation diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index af6cb847db..bc7a6ae449 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -233,7 +233,8 @@ public class LauncherSwipeHandlerV2 extends return mActivity.getFirstMatchForAppClose(launchCookieItemId, runningTaskView.getTask().key.getComponent().getPackageName(), - UserHandle.of(runningTaskView.getTask().key.userId)); + UserHandle.of(runningTaskView.getTask().key.userId), + false /* supportsAllAppsState */); } @Override diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index db92e339cd..3e7ad62233 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -135,8 +135,8 @@ public final class RecentsActivity extends StatefulActivity { SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f); SplitSelectStateController controller = - new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this), - getStateManager(), null /*depthController*/); + new SplitSelectStateController(this, mHandler, getStateManager(), + null /* depthController */); mDragLayer.recreateControllers(); mFallbackRecentsView.init(mActionsView, controller); diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index fff55a11c0..21e3ea0f2c 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -17,6 +17,7 @@ package com.android.quickstep.util; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.PendingIntent.FLAG_MUTABLE; import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -27,9 +28,11 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; +import android.text.TextUtils; import android.view.RemoteAnimationAdapter; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -45,6 +48,7 @@ import com.android.quickstep.TaskAnimationManager; import com.android.quickstep.TaskViewUtils; import com.android.quickstep.views.GroupedTaskView; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -59,12 +63,13 @@ import java.util.function.Consumer; */ public class SplitSelectStateController { + private final Context mContext; private final Handler mHandler; private final SystemUiProxy mSystemUiProxy; private final StateManager mStateManager; private final DepthController mDepthController; private @StagePosition int mStagePosition; - private PendingIntent mInitialTaskPendingIntent; + private Intent mInitialTaskIntent; private int mInitialTaskId = INVALID_TASK_ID; private int mSecondTaskId = INVALID_TASK_ID; private boolean mRecentsAnimationRunning; @@ -72,10 +77,11 @@ public class SplitSelectStateController { @Nullable private GroupedTaskView mLaunchingTaskView; - public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy, - StateManager stateManager, DepthController depthController) { + public SplitSelectStateController(Context context, Handler handler, StateManager stateManager, + DepthController depthController) { + mContext = context; mHandler = handler; - mSystemUiProxy = systemUiProxy; + mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext); mStateManager = stateManager; mDepthController = depthController; } @@ -86,12 +92,11 @@ public class SplitSelectStateController { public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) { mInitialTaskId = taskId; mStagePosition = stagePosition; - mInitialTaskPendingIntent = null; + mInitialTaskIntent = null; } - public void setInitialTaskSelect(PendingIntent pendingIntent, - @StagePosition int stagePosition) { - mInitialTaskPendingIntent = pendingIntent; + public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition) { + mInitialTaskIntent = intent; mStagePosition = stagePosition; mInitialTaskId = INVALID_TASK_ID; } @@ -99,9 +104,22 @@ public class SplitSelectStateController { /** * To be called after second task selected */ - public void setSecondTaskId(int taskId, Consumer callback) { - mSecondTaskId = taskId; - launchTasks(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, mStagePosition, + public void setSecondTask(Task task, Consumer callback) { + mSecondTaskId = task.key.id; + final Intent fillInIntent; + if (mInitialTaskIntent != null) { + fillInIntent = new Intent(); + if (TextUtils.equals(mInitialTaskIntent.getComponent().getPackageName(), + task.topActivity.getPackageName())) { + fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + } + } else { + fillInIntent = null; + } + final PendingIntent pendingIntent = + mInitialTaskIntent == null ? null : PendingIntent.getActivity(mContext, 0, + mInitialTaskIntent, FLAG_MUTABLE); + launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO); } @@ -113,18 +131,32 @@ public class SplitSelectStateController { mLaunchingTaskView = groupedTaskView; TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers = groupedTaskView.getTaskIdAttributeContainers(); - launchTasks(taskIdAttributeContainers[0].getTask().key.id, null, + launchTasks(taskIdAttributeContainers[0].getTask().key.id, taskIdAttributeContainers[1].getTask().key.id, taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList, groupedTaskView.getSplitRatio()); } /** + * To be called when we want to launch split pairs from Overview when split is initiated from + * Overview. + */ + public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition, + Consumer callback, boolean freezeTaskList, float splitRatio) { + launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2, + stagePosition, callback, freezeTaskList, splitRatio); + } + + /** + * To be called when we want to launch split pairs from Overview. Split can be initiated from + * either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a + * fill in intent with a taskId2 are set. + * @param taskPendingIntent is null when split is initiated from Overview * @param stagePosition representing location of task1 */ public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent, - int taskId2, @StagePosition int stagePosition, Consumer callback, - boolean freezeTaskList, float splitRatio) { + @Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition, + Consumer callback, boolean freezeTaskList, float splitRatio) { // Assume initial task is for top/left part of screen final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT ? new int[]{taskId1, taskId2} @@ -156,7 +188,7 @@ public class SplitSelectStateController { splitRatio, adapter); } else { mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent, - new Intent(), taskId2, mainOpts.toBundle(), null /* sideOptions */, + fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio, adapter); } } @@ -250,7 +282,7 @@ public class SplitSelectStateController { */ public void resetState() { mInitialTaskId = INVALID_TASK_ID; - mInitialTaskPendingIntent = null; + mInitialTaskIntent = null; mSecondTaskId = INVALID_TASK_ID; mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; mRecentsAnimationRunning = false; @@ -262,7 +294,7 @@ public class SplitSelectStateController { * chosen */ public boolean isSplitSelectActive() { - return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskPendingIntent != null) + return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null) && mSecondTaskId == INVALID_TASK_ID; } } diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index 04af3c11ab..d9f668dd1b 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -182,9 +182,8 @@ public class GroupedTaskView extends TaskView { @Override public void launchTask(@NonNull Consumer callback, boolean freezeTaskList) { - getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, null, - mSecondaryTask.key.id, STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, - getSplitRatio()); + getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, mSecondaryTask.key.id, + STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio()); } @Override diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 47b40157cd..6bb20fca32 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -16,7 +16,6 @@ package com.android.quickstep.views; -import static android.app.PendingIntent.FLAG_MUTABLE; import static android.view.Surface.ROTATION_0; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; @@ -67,8 +66,6 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; -import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; import android.content.LocusId; import android.content.res.Configuration; @@ -2011,22 +2008,6 @@ public abstract class RecentsView - mSplitSelectStateController.setSecondTaskId(task.key.id, - aBoolean1 -> RecentsView.this.resetFromSplitSelectionState())); + mSplitSelectStateController.setSecondTask( + task, aBoolean1 -> RecentsView.this.resetFromSplitSelectionState())); if (containerTaskView.containsMultipleTasks()) { // If we are launching from a child task, then only hide the thumbnail itself mSecondSplitHiddenView = thumbnailView; diff --git a/res/values-v31/styles.xml b/res/values-v31/styles.xml index e42d0a3371..008a77c3a8 100644 --- a/res/values-v31/styles.xml +++ b/res/values-v31/styles.xml @@ -38,6 +38,7 @@ @style/HomeSettings.PreferenceScreenStyle @style/HomeSettings.PreferenceStyle @style/HomeSettings.SwitchPreferenceStyle + google-sans-text