From 7e0f5d8a57cb7cebe9957e1c6cc2aba7c2013c0a Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Sun, 9 Oct 2022 23:39:24 -0700 Subject: [PATCH] Implement split from workspace to workspace Fixes: 239824922 Test: https://recall.googleplex.com/projects/f46cfe9c-8076-4efe-bf8a-b1cc4f1f5e1b/sessions/b745433d-cba7-4f7a-a28d-4223d9950c0b Change-Id: I9bfd5844bb9ccb6e65e77fedb7fbccb37692f812 --- .../popup/QuickstepSystemShortcut.java | 58 ++++++++++++++-- .../uioverrides/QuickstepLauncher.java | 19 ++++-- .../com/android/quickstep/SystemUiProxy.java | 28 ++++++++ .../util/SplitSelectStateController.java | 14 ++-- .../util/SplitToWorkspaceController.java | 66 +++++++++++++++++++ .../SplitWithKeyboardShortcutController.java | 27 -------- .../android/quickstep/views/RecentsView.java | 2 +- .../launcher3/config/FeatureFlags.java | 4 ++ 8 files changed, 175 insertions(+), 43 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java index 7c3281a046..7cf8201767 100644 --- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java +++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java @@ -15,20 +15,29 @@ */ package com.android.launcher3.popup; +import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition; +import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.R; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; +import com.android.quickstep.util.SplitSelectStateController; +import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.RecentsView; public interface QuickstepSystemShortcut { @@ -44,6 +53,10 @@ public interface QuickstepSystemShortcut { class SplitSelectSystemShortcut extends SystemShortcut { + private final int mSplitPlaceholderSize; + private final int mSplitPlaceholderInset; + + private final Rect mTempRect = new Rect(); private final SplitPositionOption mPosition; public SplitSelectSystemShortcut(QuickstepLauncher launcher, ItemInfo itemInfo, @@ -51,6 +64,11 @@ public interface QuickstepSystemShortcut { super(position.iconResId, position.textResId, launcher, itemInfo, originalView); mPosition = position; + + mSplitPlaceholderSize = launcher.getResources().getDimensionPixelSize( + R.dimen.split_placeholder_size); + mSplitPlaceholderInset = launcher.getResources().getDimensionPixelSize( + R.dimen.split_placeholder_inset); } @Override @@ -72,11 +90,39 @@ public interface QuickstepSystemShortcut { return; } - RecentsView recentsView = mTarget.getOverviewPanel(); StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition); - recentsView.initiateSplitSelect( - new SplitSelectSource(mOriginalView, new BitmapDrawable(bitmap), intent, - mPosition, mItemInfo, splitEvent)); + SplitSelectSource source = new SplitSelectSource(mOriginalView, + new BitmapDrawable(bitmap), intent, mPosition, mItemInfo, splitEvent); + if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + startSplitToHome(source); + } else { + RecentsView recentsView = mTarget.getOverviewPanel(); + recentsView.initiateSplitSelect(source); + } + } + + private void startSplitToHome(SplitSelectSource source) { + AbstractFloatingView.closeAllOpenViews(mTarget); + + SplitSelectStateController controller = mTarget.getSplitSelectStateController(); + controller.setInitialTaskSelect(source.intent, source.position.stagePosition, + source.itemInfo, source.splitEvent); + + RecentsView recentsView = mTarget.getOverviewPanel(); + recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds( + mSplitPlaceholderSize, mSplitPlaceholderInset, mTarget.getDeviceProfile(), + controller.getActiveSplitStagePosition(), mTempRect); + + PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration()); + RectF startingTaskRect = new RectF(); + FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(mTarget, + source.view, null /* thumbnail */, + source.drawable, startingTaskRect); + floatingTaskView.setAlpha(1); + floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect, + false /* fadeWithThumbnail */, true /* isStagedTask */); + controller.setFirstFloatingTaskView(floatingTaskView); + anim.buildAnim().start(); } } @@ -86,7 +132,7 @@ public interface QuickstepSystemShortcut { public final Drawable drawable; public final Intent intent; public final SplitPositionOption position; - public final ItemInfo mItemInfo; + public final ItemInfo itemInfo; public final StatsLogManager.EventEnum splitEvent; public SplitSelectSource(View view, Drawable drawable, Intent intent, @@ -96,7 +142,7 @@ public interface QuickstepSystemShortcut { this.drawable = drawable; this.intent = intent; this.position = position; - this.mItemInfo = itemInfo; + this.itemInfo = itemInfo; this.splitEvent = splitEvent; } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 9813c8a987..2d1a6cac38 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -96,6 +96,7 @@ import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.proxy.ProxyActivityStarter; import com.android.launcher3.proxy.StartActivityParams; @@ -136,6 +137,7 @@ import com.android.quickstep.util.QuickstepOnboardingPrefs; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteFadeOutAnimationListener; 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.views.OverviewActionsView; @@ -182,7 +184,10 @@ public class QuickstepLauncher extends Launcher { private @Nullable RotationChangeProvider mRotationChangeProvider; private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController; + private SplitSelectStateController mSplitSelectStateController; private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController; + private SplitToWorkspaceController mSplitToWorkspaceController; + /** * If Launcher restarted while in the middle of an Overview split select, it needs this data to * recover. In all other cases this will remain null. @@ -199,12 +204,14 @@ public class QuickstepLauncher extends Launcher { mActionsView = findViewById(R.id.overview_actions_view); RecentsView overviewPanel = getOverviewPanel(); - SplitSelectStateController controller = + mSplitSelectStateController = new SplitSelectStateController(this, mHandler, getStateManager(), getDepthController(), getStatsLogManager()); - overviewPanel.init(mActionsView, controller); + overviewPanel.init(mActionsView, mSplitSelectStateController); mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this, - controller); + mSplitSelectStateController); + mSplitToWorkspaceController = new SplitToWorkspaceController(this, + mSplitSelectStateController); mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize()); mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this)); @@ -330,7 +337,7 @@ public class QuickstepLauncher extends Launcher { } protected void onItemClicked(View view) { - if (!mSplitWithKeyboardShortcutController.handleSecondAppSelectionForSplit(view)) { + if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) { QuickstepLauncher.super.getItemOnClickListener().onClick(view); } } @@ -724,6 +731,10 @@ public class QuickstepLauncher extends Launcher { return mTaskbarUIController; } + public SplitSelectStateController getSplitSelectStateController() { + return mSplitSelectStateController; + } + public T getActionsView() { return (T) mActionsView; } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index ef7c6dca11..bb973342d5 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -561,6 +561,20 @@ public class SystemUiProxy implements ISystemUiProxy { } } + public void startIntents(PendingIntent pendingIntent1, Bundle options1, + PendingIntent pendingIntent2, Bundle options2, + @SplitConfigurationOptions.StagePosition int splitPosition, + float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) { + if (mSystemUiProxy != null) { + try { + mSplitScreen.startIntents(pendingIntent1, options1, pendingIntent2, options2, + splitPosition, splitRatio, remoteTransition, instanceId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startIntents"); + } + } + } + public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) { @@ -617,6 +631,20 @@ public class SystemUiProxy implements ISystemUiProxy { } } + public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, Bundle options1, + PendingIntent pendingIntent2, Bundle options2, + @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, + RemoteAnimationAdapter adapter, InstanceId instanceId) { + if (mSystemUiProxy != null) { + try { + mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, options1, + pendingIntent2, options2, sidePosition, splitRatio, adapter, instanceId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startIntentsWithLegacyTransition"); + } + } + } + public void startShortcut(String packageName, String shortcutId, int position, Bundle options, UserHandle user, InstanceId instanceId) { if (mSplitScreen != null) { diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 4a74ac663b..c263fe8656 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.RemoteAnimationAdapter; @@ -207,8 +208,8 @@ public class SplitSelectStateController { * fill in intent with a taskId2 are set. * @param intent1 is null when split is initiated from Overview * @param stagePosition representing location of task1 - * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that create - * a split instance, null for cases that bring existing instaces to the + * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that + * create a split instance, null for cases that bring existing instaces to the * foreground (quickswitch, launching previous pairs from overview) */ public void launchTasks(int taskId1, @Nullable Intent intent1, int taskId2, @@ -238,7 +239,9 @@ public class SplitSelectStateController { getOppositeStagePosition(stagePosition), splitRatio, remoteTransition, shellInstanceId); } else { - // TODO: the case when both split apps are started from an intent. + mSystemUiProxy.startIntents(getPendingIntent(intent1), options1.toBundle(), + getPendingIntent(intent2), null /* options2 */, stagePosition, + splitRatio, remoteTransition, shellInstanceId); } } else { final RemoteSplitLaunchAnimationRunner animationRunner = @@ -259,7 +262,9 @@ public class SplitSelectStateController { getOppositeStagePosition(stagePosition), splitRatio, adapter, shellInstanceId); } else { - // TODO: the case when both split apps are started from an intent. + mSystemUiProxy.startIntentsWithLegacyTransition(getPendingIntent(intent1), + options1.toBundle(), getPendingIntent(intent2), null /* options2 */, + stagePosition, splitRatio, adapter, shellInstanceId); } } } @@ -305,7 +310,6 @@ public class SplitSelectStateController { : PendingIntent.getActivity(mContext, 0, intent, FLAG_MUTABLE)); } - public @StagePosition int getActiveSplitStagePosition() { return mStagePosition; } diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java new file mode 100644 index 0000000000..e8a4b0a324 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 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; + +import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS; +import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; + +import android.content.Intent; +import android.view.View; + +import com.android.launcher3.Launcher; +import com.android.launcher3.model.data.WorkspaceItemInfo; + +/** Handles when the stage split lands on the home screen. */ +public class SplitToWorkspaceController { + + private final Launcher mLauncher; + private final SplitSelectStateController mController; + + public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) { + mLauncher = launcher; + mController = controller; + } + + /** + * Handles second app selection from stage split. If the item can't be opened in split or + * it's not in stage split state, we pass it onto Launcher's default item click handler. + */ + public boolean handleSecondAppSelectionForSplit(View view) { + if ((!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get() + && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) + || !mController.isSplitSelectActive()) { + return false; + } + Object tag = view.getTag(); + Intent intent; + if (tag instanceof WorkspaceItemInfo) { + final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag; + intent = workspaceItemInfo.intent; + } else if (tag instanceof com.android.launcher3.model.data.AppInfo) { + final com.android.launcher3.model.data.AppInfo appInfo = + (com.android.launcher3.model.data.AppInfo) tag; + intent = appInfo.intent; + } else { + return false; + } + mController.setSecondTask(intent); + mController.launchSplitTasks(aBoolean -> mLauncher.getDragLayer().removeView( + mController.getFirstFloatingTaskView())); + return true; + } +} diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java index 3587bd1da5..24d832640f 100644 --- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java +++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java @@ -101,33 +101,6 @@ public class SplitWithKeyboardShortcutController { }); } - /** - * Handles second app selection from stage split. If the item can't be opened in split or - * it's not in stage split state, we pass it onto Launcher's default item click handler. - */ - public boolean handleSecondAppSelectionForSplit(View view) { - if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get() - || !mController.isSplitSelectActive()) { - return false; - } - Object tag = view.getTag(); - Intent intent; - if (tag instanceof WorkspaceItemInfo) { - final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag; - intent = workspaceItemInfo.intent; - } else if (tag instanceof com.android.launcher3.model.data.AppInfo) { - final com.android.launcher3.model.data.AppInfo appInfo = - (com.android.launcher3.model.data.AppInfo) tag; - intent = appInfo.intent; - } else { - return false; - } - mController.setSecondTask(intent); - mController.launchSplitTasks(aBoolean -> mLauncher.getDragLayer().removeView( - mController.getFirstFloatingTaskView())); - return true; - } - public void onDestroy() { mOverviewComponentObserver.onDestroy(); } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index ff0c984b6d..ce84869932 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4232,7 +4232,7 @@ public abstract class RecentsView