Add a dev-mode freeform option to recents menu

When the developer option for freeform is enabled, provides
a "Freeform" option in the recents task overlay menu. This
is sort-of an analog to the freeform option in N recents;
however, since recents now is part of home, there's no
straightforward way to have multiple freeforms at a time,
so this really is only useful for developers.

Bug: None
Test: manual - enable freeform in dev settings, then pick
      Freeform from recent task overlay menu.
Change-Id: I5d8e95aaffb8fe43e655f705cacdd51859c95d3b
(cherry picked from commit 9beb6bf251873b51fb25432978c5b01cfd6e16b4)
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index c712703..08132aa 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -26,6 +26,8 @@
     <string name="recent_task_option_split_screen">Split screen</string>
     <!-- Title for an option to keep an app pinned to the screen until it is unpinned -->
     <string name="recent_task_option_pin">Pin</string>
+    <!-- Title for an option to enter freeform mode for a given app -->
+    <string name="recent_task_option_freeform">Freeform</string>
 
     <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_recent_apps">Overview</string>
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 59a937f..cb214af 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -46,6 +46,7 @@
             new TaskSystemShortcut.SplitScreen(),
             new TaskSystemShortcut.Pin(),
             new TaskSystemShortcut.Install(),
+            new TaskSystemShortcut.Freeform()
     };
 
     public static TaskOverlayFactory get(Context context) {
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index a8eb321..8b01e59 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
 
+import android.app.ActivityOptions;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -33,6 +34,8 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.popup.SystemShortcut;
@@ -102,19 +105,23 @@
         }
     }
 
-    public static class SplitScreen extends TaskSystemShortcut {
+    public static abstract class MultiWindow extends TaskSystemShortcut {
 
         private Handler mHandler;
 
-        public SplitScreen() {
-            super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
+        public MultiWindow(int iconRes, int textRes) {
+            super(iconRes, textRes);
             mHandler = new Handler(Looper.getMainLooper());
         }
 
+        protected abstract boolean isAvailable(BaseDraggingActivity activity);
+        protected abstract ActivityOptions makeLaunchOptions();
+        protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
+
         @Override
         public View.OnClickListener getOnClickListener(
                 BaseDraggingActivity activity, TaskView taskView) {
-            if (activity.getDeviceProfile().isMultiWindowMode) {
+            if (!isAvailable(activity)) {
                 return null;
             }
             final Task task  = taskView.getTask();
@@ -153,22 +160,13 @@
 
                 dismissTaskMenuView(activity);
 
-                final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition();
-                if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
-                    return;
-                }
-                boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
-                if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
-                        ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft))) {
-                    ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
-                    try {
-                        sysUiProxy.onSplitScreenInvoked();
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
+                ActivityOptions options = makeLaunchOptions();
+                if (options != null
+                        && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
+                                options)) {
+                    if (!onActivityStarted(activity)) {
                         return;
                     }
-                    activity.getUserEventDispatcher().logActionOnControl(TAP,
-                            LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
                     // Add a device profile change listener to kick off animating the side tasks
                     // once we enter multiwindow mode and relayout
                     activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
@@ -211,6 +209,64 @@
         }
     }
 
+    public static class SplitScreen extends MultiWindow {
+        public SplitScreen() {
+            super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
+        }
+
+        @Override
+        protected boolean isAvailable(BaseDraggingActivity activity) {
+            // Don't show menu-item if already in multi-window
+            return !activity.getDeviceProfile().isMultiWindowMode;
+        }
+
+        @Override
+        protected ActivityOptions makeLaunchOptions() {
+            final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition();
+            if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
+                return null;
+            }
+            boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
+            return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
+        }
+
+        @Override
+        protected boolean onActivityStarted(BaseDraggingActivity activity) {
+            ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
+            try {
+                sysUiProxy.onSplitScreenInvoked();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
+                return false;
+            }
+            activity.getUserEventDispatcher().logActionOnControl(TAP,
+                    LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
+            return true;
+        }
+    }
+
+    public static class Freeform extends MultiWindow {
+        public Freeform() {
+            super(R.drawable.ic_split_screen, R.string.recent_task_option_freeform);
+        }
+
+        @Override
+        protected boolean isAvailable(BaseDraggingActivity activity) {
+            return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
+        }
+
+        @Override
+        protected ActivityOptions makeLaunchOptions() {
+            return ActivityOptionsCompat.makeFreeformOptions();
+        }
+
+        @Override
+        protected boolean onActivityStarted(BaseDraggingActivity activity) {
+            Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL);
+            return true;
+        }
+    }
+
     public static class Pin extends TaskSystemShortcut {
 
         private static final String TAG = Pin.class.getSimpleName();