Merge "Launch initial split from taskbar in overview app menu" into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index 45d1b11..184ea71 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -15,38 +15,14 @@
*/
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.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-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 androidx.annotation.Nullable;
-
-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.splitscreen.SplitShortcut;
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;
-import com.android.systemui.shared.recents.model.Task;
-import java.util.function.Consumer;
-
+/** {@link SystemShortcut.Factory} implementation to create workspace split shortcuts */
public interface QuickstepSystemShortcut {
String TAG = QuickstepSystemShortcut.class.getSimpleName();
@@ -58,116 +34,12 @@
originalView, position);
}
- class SplitSelectSystemShortcut extends SystemShortcut<QuickstepLauncher> {
-
- private final int mSplitPlaceholderSize;
- private final int mSplitPlaceholderInset;
-
- private final Rect mTempRect = new Rect();
- private final SplitPositionOption mPosition;
+ class SplitSelectSystemShortcut extends SplitShortcut<QuickstepLauncher> {
public SplitSelectSystemShortcut(QuickstepLauncher launcher, ItemInfo itemInfo,
View originalView, SplitPositionOption position) {
- 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
- public void onClick(View view) {
- // Initiate splitscreen from the Home screen or Home All Apps
- Bitmap bitmap;
- Intent intent;
- if (mItemInfo instanceof WorkspaceItemInfo) {
- final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
- bitmap = workspaceItemInfo.bitmap.icon;
- intent = workspaceItemInfo.intent;
- } else if (mItemInfo instanceof com.android.launcher3.model.data.AppInfo) {
- final com.android.launcher3.model.data.AppInfo appInfo =
- (com.android.launcher3.model.data.AppInfo) mItemInfo;
- bitmap = appInfo.bitmap.icon;
- intent = appInfo.intent;
- } else {
- Log.e(TAG, "unknown item type");
- return;
- }
-
- StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
- RecentsView recentsView = mTarget.getOverviewPanel();
- // Check if there is already an instance of this app running, if so, initiate the split
- // using that.
- recentsView.findLastActiveTaskAndRunCallback(
- intent.getComponent(),
- (Consumer<Task>) foundTask -> {
- SplitSelectSource source = new SplitSelectSource(mOriginalView,
- new BitmapDrawable(bitmap), intent, mPosition, mItemInfo,
- splitEvent, foundTask);
- if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
- startSplitToHome(source);
- } else {
- 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, source.alreadyRunningTask);
-
- 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();
- final 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.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- mTarget.getDragLayer().removeView(floatingTaskView);
- controller.resetState();
- }
- });
- anim.buildAnim().start();
- }
- }
-
- class SplitSelectSource {
-
- public final View view;
- public final Drawable drawable;
- public final Intent intent;
- public final SplitPositionOption position;
- public final ItemInfo itemInfo;
- public final StatsLogManager.EventEnum splitEvent;
- @Nullable
- public final Task alreadyRunningTask;
-
- public SplitSelectSource(View view, Drawable drawable, Intent intent,
- SplitPositionOption position, ItemInfo itemInfo,
- StatsLogManager.EventEnum splitEvent, @Nullable Task foundTask) {
- this.view = view;
- this.drawable = drawable;
- this.intent = intent;
- this.position = position;
- this.itemInfo = itemInfo;
- this.splitEvent = splitEvent;
- this.alreadyRunningTask = foundTask;
+ super(position.iconResId, position.textResId, launcher, itemInfo, originalView,
+ position);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt b/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt
new file mode 100644
index 0000000..20c8c44
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.launcher3.splitscreen
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.util.Log
+import android.view.View
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.popup.QuickstepSystemShortcut
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * Shortcut to allow starting split. Default interaction for [onClick] is to launch
+ * split selection mode
+ */
+abstract class SplitShortcut<T>(
+ iconResId: Int,
+ labelResId: Int,
+ target: T,
+ itemInfo: ItemInfo?,
+ originalView: View?,
+ protected val position: SplitPositionOption
+) : SystemShortcut<T>(iconResId, labelResId, target, itemInfo, originalView) where
+T : Context?,
+T : ActivityContext? {
+ private val TAG = SystemShortcut::class.java.simpleName
+
+ // Initiate splitscreen from the Home screen or Home All Apps
+ protected val splitSelectSource: SplitSelectSource?
+ get() {
+ // Initiate splitscreen from the Home screen or Home All Apps
+ val bitmap: Bitmap
+ val intent: Intent
+ when (mItemInfo) {
+ is WorkspaceItemInfo -> {
+ val workspaceItemInfo = mItemInfo
+ bitmap = workspaceItemInfo.bitmap.icon
+ intent = workspaceItemInfo.intent
+ }
+ is com.android.launcher3.model.data.AppInfo -> {
+ val appInfo = mItemInfo
+ bitmap = appInfo.bitmap.icon
+ intent = appInfo.intent
+ }
+ else -> {
+ Log.e(TAG, "unknown item type")
+ return null
+ }
+ }
+ val splitEvent =
+ SplitConfigurationOptions.getLogEventForPosition(position.stagePosition)
+ return SplitSelectSource(
+ mOriginalView,
+ BitmapDrawable(bitmap),
+ intent,
+ position,
+ mItemInfo,
+ splitEvent
+ )
+ }
+
+ /** Starts split selection on the provided [mTarget] */
+ override fun onClick(view: View?) {
+ val splitSelectSource = splitSelectSource
+ if (splitSelectSource == null) {
+ Log.w(QuickstepSystemShortcut.TAG, "no split selection source")
+ return
+ }
+ mTarget!!.startSplitSelection(splitSelectSource)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8a5b2c5..accbf21 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -92,6 +93,7 @@
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
@@ -754,6 +756,11 @@
}
}
+ @Override
+ public void startSplitSelection(SplitSelectSource splitSelectSource) {
+ mControllers.uiController.startSplitSelection(splitSelectSource);
+ }
+
protected void onTaskbarIconClicked(View view) {
Object tag = view.getTag();
if (tag instanceof Task) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index d9773d4..62f1293 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -125,7 +125,8 @@
mControllers.taskbarDragController.setDisallowLongClick(disallowLongClick);
mControllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag);
mControllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick);
- mControllers.taskbarPopupController.setHideSplitOptions(disallowGlobalDrag);
+ mControllers.taskbarPopupController.setAllowInitialSplitSelection(
+ disallowGlobalDrag);
}
};
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 9b27c9d..30d6eb4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -17,11 +17,9 @@
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
-import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Point;
-import android.os.Bundle;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
@@ -46,6 +44,7 @@
import com.android.launcher3.popup.PopupLiveUpdateHandler;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.splitscreen.SplitShortcut;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.PackageUserKey;
@@ -75,7 +74,7 @@
// Initialized in init.
private TaskbarControllers mControllers;
- private boolean mHideSplitOptions;
+ private boolean mAllowInitialSplitSelection;
public TaskbarPopupController(TaskbarActivityContext context) {
mContext = context;
@@ -101,8 +100,8 @@
mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
}
- public void setHideSplitOptions(boolean hideSplitOptions) {
- mHideSplitOptions = hideSplitOptions;
+ public void setAllowInitialSplitSelection(boolean allowInitialSplitSelection) {
+ mAllowInitialSplitSelection = allowInitialSplitSelection;
}
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
@@ -188,13 +187,9 @@
}
// Create a Stream of all applicable system shortcuts
- // TODO(b/227800345): Add "Split bottom" option when tablet is in portrait mode.
private Stream<SystemShortcut.Factory> getSystemShortcuts() {
// concat a Stream of split options with a Stream of APP_INFO
Stream<SystemShortcut.Factory> appInfo = Stream.of(APP_INFO);
- if (mHideSplitOptions) {
- return appInfo;
- }
return Stream.concat(
Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
@@ -261,7 +256,7 @@
private SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
SplitPositionOption position) {
return (context, itemInfo, originalView) -> new TaskbarSplitShortcut(context, itemInfo,
- originalView, position);
+ originalView, position, mAllowInitialSplitSelection);
}
/**
@@ -269,32 +264,43 @@
* from the taskbar, as if the user performed a drag and drop split.
* Includes an onClick method that initiates the actual split.
*/
- private static class TaskbarSplitShortcut extends SystemShortcut<BaseTaskbarContext> {
- private final SplitPositionOption mPosition;
+ private static class TaskbarSplitShortcut extends
+ SplitShortcut<BaseTaskbarContext> {
+ /**
+ * If {@code true}, clicking this shortcut will not attempt to start a split app directly,
+ * but be the first app in split selection mode
+ */
+ private final boolean mAllowInitialSplitSelection;
- TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo, View originalView,
- SplitPositionOption position) {
- super(position.iconResId, position.textResId, context, itemInfo, originalView);
- mPosition = position;
- }
+ TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo, View originalView,
+ SplitPositionOption position, boolean allowInitialSplitSelection) {
+ super(position.iconResId, position.textResId, context, itemInfo, originalView,
+ position);
+ mAllowInitialSplitSelection = allowInitialSplitSelection;
+ }
@Override
public void onClick(View view) {
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+ if (mAllowInitialSplitSelection) {
+ super.onClick(view);
+ return;
+ }
+
// Initiate splitscreen from the in-app Taskbar or Taskbar All Apps
Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
LogUtils.getShellShareableInstanceId();
mTarget.getStatsLogManager().logger()
.withItemInfo(mItemInfo)
.withInstanceId(instanceIds.second)
- .log(getLogEventForPosition(mPosition.stagePosition));
+ .log(getLogEventForPosition(getPosition().stagePosition));
- AbstractFloatingView.closeAllOpenViews(mTarget);
if (mItemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
SystemUiProxy.INSTANCE.get(mTarget).startShortcut(
workspaceItemInfo.getIntent().getPackage(),
workspaceItemInfo.getDeepShortcutId(),
- mPosition.stagePosition,
+ getPosition().stagePosition,
null,
workspaceItemInfo.user,
instanceIds.first);
@@ -305,7 +311,7 @@
null,
mItemInfo.user),
new Intent(),
- mPosition.stagePosition,
+ getPosition().stagePosition,
null,
instanceIds.first);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index ebb37a8..1c11bf7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.taskbar;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent;
@@ -26,6 +28,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -165,6 +168,23 @@
return null;
}
+ public void startSplitSelection(SplitConfigurationOptions.SplitSelectSource splitSelectSource) {
+ RecentsView recentsView = getRecentsView();
+ if (recentsView == null) {
+ return;
+ }
+ recentsView.findLastActiveTaskAndRunCallback(
+ splitSelectSource.intent.getComponent(),
+ (Consumer<Task>) foundTask -> {
+ splitSelectSource.alreadyRunningTaskId = foundTask == null
+ ? INVALID_TASK_ID
+ : foundTask.key.id;
+ splitSelectSource.animateCurrentTaskDismissal = foundTask != null;
+ recentsView.initiateSplitSelect(splitSelectSource);
+ }
+ );
+ }
+
/**
* Uses the clicked Taskbar icon to launch a second app for splitscreen.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index d7a0b03..3edb375 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.taskbar.overlay;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import android.content.Context;
import android.view.View;
@@ -28,7 +30,13 @@
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.function.Consumer;
/**
* Window context for the taskbar overlays such as All Apps and EDU.
@@ -46,6 +54,7 @@
// We automatically stash taskbar when All Apps is opened in gesture navigation mode.
private final boolean mWillTaskbarBeVisuallyStashed;
private final int mStashedTaskbarHeight;
+ private final TaskbarUIController mUiController;
public TaskbarOverlayContext(
Context windowContext,
@@ -61,6 +70,8 @@
TaskbarStashController taskbarStashController = controllers.taskbarStashController;
mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
+
+ mUiController = controllers.uiController;
}
boolean willTaskbarBeVisuallyStashed() {
@@ -111,6 +122,11 @@
}
@Override
+ public void startSplitSelection(SplitSelectSource splitSelectSource) {
+ mUiController.startSplitSelection(splitSelectSource);
+ }
+
+ @Override
public DotInfo getDotInfoForItem(ItemInfo info) {
return mTaskbarContext.getDotInfoForItem(info);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 30850b9..abbd01d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.uioverrides;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
@@ -32,6 +33,7 @@
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_WIDGET_PICKER_DEPTH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
@@ -46,8 +48,11 @@
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
@@ -57,6 +62,8 @@
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
import android.media.permission.SafeCloseable;
@@ -80,6 +87,7 @@
import androidx.annotation.Nullable;
import com.android.app.viewcapture.ViewCapture;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -92,6 +100,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
@@ -133,6 +142,7 @@
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.quickstep.OverviewCommandHelper;
@@ -150,9 +160,11 @@
import com.android.quickstep.util.SplitWithKeyboardShortcutController;
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.DesktopTaskView;
+import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.unfold.UnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
@@ -168,6 +180,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -362,7 +375,7 @@
Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
if (ENABLE_SPLIT_FROM_WORKSPACE.get() && mDeviceProfile.isTablet) {
RecentsView recentsView = getOverviewPanel();
- // TODO: Pull it out of PagedOrentationHandler for split from workspace.
+ // TODO(b/266482558): Pull it out of PagedOrentationHandler for split from workspace.
List<SplitPositionOption> positions =
recentsView.getPagedOrientationHandler().getSplitPositionOptions(
mDeviceProfile);
@@ -549,6 +562,64 @@
}
@Override
+ public void startSplitSelection(SplitSelectSource splitSelectSource) {
+ RecentsView recentsView = getOverviewPanel();
+ // Check if there is already an instance of this app running, if so, initiate the split
+ // using that.
+ recentsView.findLastActiveTaskAndRunCallback(
+ splitSelectSource.intent.getComponent(),
+ (Consumer<Task>) foundTask -> {
+ splitSelectSource.alreadyRunningTaskId = foundTask == null
+ ? INVALID_TASK_ID
+ : foundTask.key.id;
+ if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ startSplitToHome(splitSelectSource);
+ } else {
+ recentsView.initiateSplitSelect(splitSelectSource);
+ }
+ }
+ );
+ }
+
+ /** TODO(b/266482558) Migrate into SplitSelectStateController or someplace split specific. */
+ private void startSplitToHome(
+ SplitSelectSource source) {
+ AbstractFloatingView.closeAllOpenViews(this);
+ int splitPlaceholderSize = getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_size);
+ int splitPlaceholderInset = getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_inset);
+ Rect tempRect = new Rect();
+
+ SplitSelectStateController controller = getSplitSelectStateController();
+ controller.setInitialTaskSelect(source.intent, source.position.stagePosition,
+ source.itemInfo, source.splitEvent, source.alreadyRunningTaskId);
+
+ RecentsView recentsView = getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+ splitPlaceholderSize, splitPlaceholderInset, getDeviceProfile(),
+ controller.getActiveSplitStagePosition(), tempRect);
+
+ PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
+ RectF startingTaskRect = new RectF();
+ final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(this,
+ source.view, null /* thumbnail */, source.drawable, startingTaskRect);
+ floatingTaskView.setAlpha(1);
+ floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+ controller.setFirstFloatingTaskView(floatingTaskView);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ getDragLayer().removeView(floatingTaskView);
+ controller.resetState();
+ }
+ });
+ anim.buildAnim().start();
+ }
+
+
+ @Override
protected void onResume() {
super.onResume();
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index bcaae99..db6d56b 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -34,9 +34,9 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
@@ -269,7 +269,7 @@
}
@Override
- public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
+ public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
super.initiateSplitSelect(splitSelectSource);
mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 681f068..e5d54d7 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -90,6 +90,8 @@
private Intent mSecondTaskIntent;
private int mSecondTaskId = INVALID_TASK_ID;
private boolean mRecentsAnimationRunning;
+ /** If {@code true}, animates the existing task view split placeholder view */
+ private boolean mAnimateCurrentTaskDismissal;
@Nullable
private UserHandle mUser;
/** If not null, this is the TaskView we want to launch from */
@@ -111,22 +113,15 @@
}
/**
- * To be called after first task selected in Overview.
+ * @param alreadyRunningTask if set to {@link android.app.ActivityTaskManager#INVALID_TASK_ID}
+ * then @param intent will be used to launch the initial task
+ * @param intent will be ignored if @param alreadyRunningTask is set
*/
- public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
- StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
- mInitialTaskId = task.key.id;
- setInitialData(stagePosition, splitEvent, itemInfo);
- }
-
- /**
- * To be called after first task selected from home or all apps.
- */
- public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
+ public void setInitialTaskSelect(@Nullable Intent intent, @StagePosition int stagePosition,
@NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent,
- @Nullable Task alreadyRunningTask) {
- if (alreadyRunningTask != null) {
- mInitialTaskId = alreadyRunningTask.key.id;
+ int alreadyRunningTask) {
+ if (alreadyRunningTask != INVALID_TASK_ID) {
+ mInitialTaskId = alreadyRunningTask;
} else {
mInitialTaskIntent = intent;
mUser = itemInfo.user;
@@ -348,6 +343,14 @@
return null;
}
+ public boolean isAnimateCurrentTaskDismissal() {
+ return mAnimateCurrentTaskDismissal;
+ }
+
+ public void setAnimateCurrentTaskDismissal(boolean animateCurrentTaskDismissal) {
+ mAnimateCurrentTaskDismissal = animateCurrentTaskDismissal;
+ }
+
/**
* Requires Shell Transitions
*/
@@ -454,6 +457,7 @@
mLaunchingTaskView = null;
mItemInfo = null;
mSplitEvent = null;
+ mAnimateCurrentTaskDismissal = false;
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index cf033a6..886fc80 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -34,13 +34,13 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.LauncherState;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.util.SplitSelectStateController;
@@ -199,7 +199,7 @@
}
@Override
- public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
+ public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
super.initiateSplitSelect(splitSelectSource);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index cdc0574..8facb0a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Surface.ROTATION_0;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
@@ -144,7 +145,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -156,6 +156,7 @@
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
@@ -677,7 +678,7 @@
private SplitInstructionsView mSplitInstructionsView;
@Nullable
- private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource;
+ private SplitSelectSource mSplitSelectSource;
/**
* Keeps track of the index of the TaskView that split screen was initialized with so we know
@@ -1607,8 +1608,9 @@
// If we are entering Overview as a result of initiating a split from somewhere else
// (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
- Task stagedTaskToBeRemovedFromGrid =
- mSplitSelectSource != null ? mSplitSelectSource.alreadyRunningTask : null;
+ int stagedTaskIdToBeRemovedFromGrid = mSplitSelectSource != null
+ ? mSplitSelectSource.alreadyRunningTaskId
+ : INVALID_TASK_ID;
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
@@ -1617,8 +1619,8 @@
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
- && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);
+ boolean isRemovalNeeded = stagedTaskIdToBeRemovedFromGrid != INVALID_TASK_ID
+ && groupTask.containsTask(stagedTaskIdToBeRemovedFromGrid);
TaskView taskView;
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
@@ -1632,7 +1634,7 @@
addView(taskView);
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
- if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
+ if (groupTask.task1.key.id == stagedTaskIdToBeRemovedFromGrid) {
taskView.bind(groupTask.task2, mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
@@ -3019,7 +3021,7 @@
RectF startingTaskRect = new RectF();
safeRemoveDragLayerView(mFirstFloatingTaskView);
- if (mSplitHiddenTaskView != null) {
+ if (mSplitSelectStateController.isAnimateCurrentTaskDismissal()) {
// Create the split select animation from Overview
mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
anim.setViewAlpha(mSplitHiddenTaskView.getIconView(), 0, clampToProgress(LINEAR,
@@ -4393,29 +4395,38 @@
initiateSplitSelect(taskView, defaultSplitPosition, LAUNCHER_OVERVIEW_ACTIONS_SPLIT);
}
+ /** TODO(b/266477929): Consolidate this call w/ the one below */
public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent) {
mSplitHiddenTaskView = taskView;
- mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
- stagePosition, splitEvent, taskView.getItemInfo());
+ mSplitSelectStateController.setInitialTaskSelect(null /*intent*/,
+ stagePosition, taskView.getItemInfo(), splitEvent, taskView.mTask.key.id);
+ mSplitSelectStateController.setAnimateCurrentTaskDismissal(
+ true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
}
/**
- * Called when staging a split from Home/AllApps, using the icon long-press menu.
+ * Called when staging a split from Home/AllApps/Overview (Taskbar),
+ * using the icon long-press menu.
+ * Attempts to initiate split with an existing taskView, if one exists
*/
- public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
+ public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
mSplitSelectSource = splitSelectSource;
+ mSplitHiddenTaskView = getTaskViewByTaskId(splitSelectSource.alreadyRunningTaskId);
+ mSplitHiddenTaskViewIndex = indexOfChild(mSplitHiddenTaskView);
+ mSplitSelectStateController
+ .setAnimateCurrentTaskDismissal(splitSelectSource.animateCurrentTaskDismissal);
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
- splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTask);
+ splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
}
/**
* Modifies a PendingAnimation with the animations for entering split staging
*/
public void createSplitSelectInitAnimation(PendingAnimation builder, int duration) {
- if (mSplitHiddenTaskView != null) {
+ if (mSplitSelectStateController.isAnimateCurrentTaskDismissal()) {
// Splitting from Overview
createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
true /* dismissingForSplitSelection*/);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 0e25984c..69bba69 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -40,6 +40,7 @@
public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
implements View.OnClickListener {
+ private static final String TAG = SystemShortcut.class.getSimpleName();
private final int mIconResId;
protected final int mLabelResId;
protected int mAccessibilityActionId;
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 19a3948..8c5e782 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -21,11 +21,15 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.content.Intent;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.View;
import androidx.annotation.IntDef;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
import java.lang.annotation.Retention;
@@ -190,4 +194,35 @@
return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT
: STAGE_POSITION_TOP_OR_LEFT;
}
+
+ public static class SplitSelectSource {
+
+ /** Keep in sync w/ ActivityTaskManager#INVALID_TASK_ID (unreference-able) */
+ private static final int INVALID_TASK_ID = -1;
+
+ public final View view;
+ public final Drawable drawable;
+ public final Intent intent;
+ public final SplitPositionOption position;
+ public final ItemInfo itemInfo;
+ public final StatsLogManager.EventEnum splitEvent;
+ /** Represents the taskId of the first app to start in split screen */
+ public int alreadyRunningTaskId = INVALID_TASK_ID;
+ /**
+ * If {@code true}, animates the view represented by {@link #alreadyRunningTaskId} into the
+ * split placeholder view
+ */
+ public boolean animateCurrentTaskDismissal;
+
+ public SplitSelectSource(View view, Drawable drawable, Intent intent,
+ SplitPositionOption position, ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent) {
+ this.view = view;
+ this.drawable = drawable;
+ this.intent = intent;
+ this.position = position;
+ this.itemInfo = itemInfo;
+ this.splitEvent = splitEvent;
+ }
+ }
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 79b4cb4..d794f36 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -70,6 +70,7 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.ViewCache;
import java.util.List;
@@ -130,6 +131,12 @@
return null;
}
+ /** Called when the first app in split screen has been selected */
+ default void startSplitSelection(
+ SplitConfigurationOptions.SplitSelectSource splitSelectSource) {
+ // Overridden, intentionally empty
+ }
+
/**
* The root view to support drag-and-drop and popup support.
*/