Implement splitting from Taskbar long-press

Implements the ability to split the screen by long-pressing a Taskbar icon.

Bug: 217964720
Test: Manual
Change-Id: I5d324d8ca912c10cc3c3de21cae98fad546b85ac
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index 3bc6576..cc0072e 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -40,16 +40,12 @@
 
     class SplitSelectSystemShortcut extends SystemShortcut<BaseQuickstepLauncher> {
 
-        private final BaseQuickstepLauncher mLauncher;
-        private final ItemInfo mItemInfo;
         private final SplitPositionOption mPosition;
 
         public SplitSelectSystemShortcut(BaseQuickstepLauncher launcher, ItemInfo itemInfo,
                 SplitPositionOption position) {
             super(position.iconResId, position.textResId, launcher, itemInfo);
 
-            mLauncher = launcher;
-            mItemInfo = itemInfo;
             mPosition = position;
         }
 
@@ -71,7 +67,7 @@
                 return;
             }
 
-            RecentsView recentsView = mLauncher.getOverviewPanel();
+            RecentsView recentsView = mTarget.getOverviewPanel();
             recentsView.initiateSplitSelect(
                     new SplitSelectSource(view, new BitmapDrawable(bitmap), intent, mPosition));
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index f9c8062..1ccad78 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,14 +15,19 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.graphics.Point;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -38,7 +43,9 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.SystemUiProxy;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -151,7 +158,7 @@
                 mPopupDataProvider.getShortcutCountForItem(item),
                 mPopupDataProvider.getNotificationKeysForItem(item),
                 // TODO (b/198438631): add support for INSTALL shortcut factory
-                Stream.of(APP_INFO)
+                getSystemShortcuts()
                         .map(s -> s.getShortcut(context, item))
                         .filter(Objects::nonNull)
                         .collect(Collectors.toList()));
@@ -167,6 +174,18 @@
         return container;
     }
 
+    // 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
+        return Stream.concat(
+                Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
+                        .stream()
+                        .map(this::createSplitShortcutFactory),
+                Stream.of(APP_INFO)
+        );
+    }
+
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarPopupController:");
@@ -213,4 +232,56 @@
             return false;
         }
     }
+
+    /**
+     * Creates a factory function representing a single "split position" menu item ("Split left,"
+     * "Split right," or "Split top").
+     * @param position A SplitPositionOption representing whether we are splitting top, left, or
+     *                 right.
+     * @return A factory function to be used in populating the long-press menu.
+     */
+    private SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
+            SplitPositionOption position) {
+        return (context, itemInfo) -> new TaskbarSplitShortcut(context, itemInfo, position);
+    }
+
+     /**
+     * A single menu item ("Split left," "Split right," or "Split top") that executes a split
+     * 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;
+
+        TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo,
+                SplitPositionOption position) {
+            super(position.iconResId, position.textResId, context, itemInfo);
+            mPosition = position;
+        }
+
+        @Override
+        public void onClick(View view) {
+            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,
+                        null,
+                        workspaceItemInfo.user);
+            } else {
+                SystemUiProxy.INSTANCE.get(mTarget).startIntent(
+                        mTarget.getSystemService(LauncherApps.class).getMainActivityLaunchIntent(
+                                mItemInfo.getIntent().getComponent(),
+                                null,
+                                mItemInfo.user),
+                        new Intent(),
+                        mPosition.stagePosition,
+                        null);
+            }
+        }
+    }
 }
+
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9bc3d15..655ab5a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,6 +18,9 @@
 
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ICON_BADGED;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
@@ -86,12 +89,14 @@
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
@@ -867,4 +872,39 @@
         v.getLocationOnScreen(pos);
         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
     }
+
+    /**
+     * Returns a list of screen-splitting options depending on the device orientation (split top for
+     * portrait, split left for landscape, split left and right for landscape tablets, etc.)
+     */
+    public static List<SplitPositionOption> getSplitPositionOptions(
+            DeviceProfile dp) {
+        List<SplitPositionOption> options = new ArrayList<>();
+        // Add both left and right options if we're in tablet mode
+        if (dp.isTablet && dp.isLandscape) {
+            options.add(new SplitPositionOption(
+                    R.drawable.ic_split_left, R.string.split_screen_position_left,
+                    STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+            options.add(new SplitPositionOption(
+                    R.drawable.ic_split_right, R.string.split_screen_position_right,
+                    STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
+        } else {
+            if (dp.isSeascape()) {
+                // Add left/right options
+                options.add(new SplitPositionOption(
+                        R.drawable.ic_split_right, R.string.split_screen_position_right,
+                        STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
+            } else if (dp.isLandscape) {
+                options.add(new SplitPositionOption(
+                        R.drawable.ic_split_left, R.string.split_screen_position_left,
+                        STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+            } else {
+                // Only add top option
+                options.add(new SplitPositionOption(
+                        R.drawable.ic_split_top, R.string.split_screen_position_top,
+                        STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+            }
+        }
+        return options;
+    }
 }
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index d88656c..73c6115 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -29,7 +29,6 @@
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
 import android.graphics.Matrix;
@@ -48,7 +47,6 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
@@ -56,7 +54,6 @@
 import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.launcher3.views.BaseDragLayer;
 
-import java.util.ArrayList;
 import java.util.List;
 
 public class PortraitPagedViewHandler implements PagedOrientationHandler {
@@ -406,33 +403,7 @@
 
     @Override
     public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
-        List<SplitPositionOption> options = new ArrayList<>(1);
-        // Add both left and right options if we're in tablet mode
-        if (dp.isTablet && dp.isLandscape) {
-            options.add(new SplitPositionOption(
-                    R.drawable.ic_split_left, R.string.split_screen_position_left,
-                    STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
-            options.add(new SplitPositionOption(
-                    R.drawable.ic_split_right, R.string.split_screen_position_right,
-                    STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
-        } else {
-            if (dp.isSeascape()) {
-                // Add left/right options
-                options.add(new SplitPositionOption(
-                        R.drawable.ic_split_right, R.string.split_screen_position_right,
-                        STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
-            } else if (dp.isLandscape) {
-                options.add(new SplitPositionOption(
-                        R.drawable.ic_split_left, R.string.split_screen_position_left,
-                        STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
-            } else {
-                // Only add top option
-                options.add(new SplitPositionOption(
-                        R.drawable.ic_split_top, R.string.split_screen_position_top,
-                        STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
-            }
-        }
-        return options;
+        return Utilities.getSplitPositionOptions(dp);
     }
 
     @Override