Use pinned taskbar with desktop tasks on freeform displays

Bug: 390665752
Flag: com.android.window.flags.enable_desktop_taskbar_on_freeform_displays
Test: manual on desktop device. verify correct taskbar is shown
      on home screen, in overview, in desktop mode, with fullscreen
      app shown. Verify opening/launching apps from taskbar works
      as expected.

Change-Id: I5c1e21799609c28ec44cc190bfc681934907199f
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
index 8806bc6..cb399e8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
@@ -50,8 +50,9 @@
 
     fun shouldShowDesktopTasksInTaskbar(): Boolean {
         return desktopVisibilityController.areDesktopTasksVisible() ||
-            DisplayController.showLockedTaskbarOnHome(context) &&
-                taskbarControllers.taskbarStashController.isOnHome
+            DisplayController.showDesktopTaskbarForFreeformDisplay(context) ||
+            (DisplayController.showLockedTaskbarOnHome(context) &&
+                taskbarControllers.taskbarStashController.isOnHome)
     }
 
     fun getTaskbarCornerRoundness(doesAnyTaskRequireTaskbarRounding: Boolean): Float {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 9a9575d..cada5a3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -58,7 +58,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -714,6 +713,10 @@
 
     private static boolean shouldShowTaskbar(Context context, boolean isInLauncher,
             boolean isInOverview) {
+        if (DisplayController.showDesktopTaskbarForFreeformDisplay(context)) {
+            return true;
+        }
+
         if (DisplayController.showLockedTaskbarOnHome(context) && isInLauncher) {
             return true;
         }
@@ -769,9 +772,14 @@
      * This refers to the intended state - a transition to this state might be in progress.
      */
     public boolean isTaskbarAlignedWithHotseat() {
+        if (DisplayController.showDesktopTaskbarForFreeformDisplay(mLauncher)) {
+            return false;
+        }
+
         if (DisplayController.showLockedTaskbarOnHome(mLauncher) && isInLauncher()) {
             return false;
         }
+
         return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 1ca3dfb..2ded1bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -301,7 +301,8 @@
         // TODO(b/390665752): Feature to "lock" pinned taskbar to home screen will be superseded by
         //     pinning, in other launcher states, at which point this variable can be removed.
         mInAppStateAffectsDesktopTasksVisibilityInTaskbar =
-                DisplayController.showLockedTaskbarOnHome(mActivity);
+                !DisplayController.showDesktopTaskbarForFreeformDisplay(mActivity)
+                        && DisplayController.showLockedTaskbarOnHome(mActivity);
 
         mTaskbarBackgroundDuration = activity.getResources().getInteger(
                 R.integer.taskbar_background_duration);
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 4d56c63..10ae7a3 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.util.window.CachedDisplayInfo;
 import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.quickstep.SystemUiProxy;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 import java.util.List;
@@ -90,6 +91,25 @@
     }
 
     @Override
+    public boolean showDesktopTaskbarForFreeformDisplay(Context displayInfoContext) {
+        if (!DesktopModeStatus.canEnterDesktopMode(displayInfoContext)) {
+            return false;
+        }
+
+        if (!DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(displayInfoContext)) {
+            return false;
+        }
+
+        if (!Flags.enableDesktopTaskbarOnFreeformDisplays()) {
+            return false;
+        }
+
+        final boolean isFreeformDisplay = displayInfoContext.getResources().getConfiguration()
+                .windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+        return isFreeformDisplay;
+    }
+
+    @Override
     public boolean isHomeVisible(Context context) {
         return SystemUiProxy.INSTANCE.get(context).getHomeVisibilityState().isHomeVisible();
     }
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 475dc04..ee1af81 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -220,6 +220,14 @@
         return INSTANCE.get(context).getInfo().showLockedTaskbarOnHome();
     }
 
+    /**
+     * Returns whether desktop taskbar (pinned taskbar that shows desktop tasks) is to be used
+     * on the display because the display is a freeform display.
+     */
+    public static boolean showDesktopTaskbarForFreeformDisplay(Context context) {
+        return INSTANCE.get(context).getInfo().showDesktopTaskbarForFreeformDisplay();
+    }
+
     @Override
     public void onDesktopVisibilityChanged(boolean visible) {
         notifyConfigChange();
@@ -259,7 +267,9 @@
                         new PortraitSize(config.screenHeightDp, config.screenWidthDp))
                 || mWindowContext.getDisplay().getRotation() != mInfo.rotation
                 || mWMProxy.showLockedTaskbarOnHome(mWindowContext)
-                        != mInfo.showLockedTaskbarOnHome()) {
+                        != mInfo.showLockedTaskbarOnHome()
+                || mWMProxy.showDesktopTaskbarForFreeformDisplay(mWindowContext)
+                        != mInfo.showDesktopTaskbarForFreeformDisplay()) {
             notifyConfigChange();
         }
     }
@@ -376,6 +386,8 @@
         private final boolean mShowLockedTaskbarOnHome;
         private final boolean mIsHomeVisible;
 
+        private final boolean mShowDesktopTaskbarForFreeformDisplay;
+
         public Info(Context displayInfoContext) {
             /* don't need system overrides for external displays */
             this(displayInfoContext, new WindowManagerProxy(), new ArrayMap<>());
@@ -438,6 +450,8 @@
                     TASKBAR_PINNING_IN_DESKTOP_MODE);
             mIsInDesktopMode = wmProxy.isInDesktopMode();
             mShowLockedTaskbarOnHome = wmProxy.showLockedTaskbarOnHome(displayInfoContext);
+            mShowDesktopTaskbarForFreeformDisplay = wmProxy.showDesktopTaskbarForFreeformDisplay(
+                    displayInfoContext);
             mIsHomeVisible = wmProxy.isHomeVisible(displayInfoContext);
         }
 
@@ -455,6 +469,11 @@
                 return sTransientTaskbarStatusForTests;
             }
             if (enableTaskbarPinning()) {
+                // If "freeform" display taskbar is enabled, ensure the taskbar is pinned.
+                if (mShowDesktopTaskbarForFreeformDisplay) {
+                    return false;
+                }
+
                 // If Launcher is visible on the freeform display, ensure the taskbar is pinned.
                 if (mShowLockedTaskbarOnHome && mIsHomeVisible) {
                     return false;
@@ -533,6 +552,14 @@
         public boolean showLockedTaskbarOnHome() {
             return mShowLockedTaskbarOnHome;
         }
+
+        /**
+         * Returns whether the taskbar should be pinned, and showing desktop tasks, because the
+         * display is a "freeform" display.
+         */
+        public boolean showDesktopTaskbarForFreeformDisplay() {
+            return mShowDesktopTaskbarForFreeformDisplay;
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index e568eed..f511ef2 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -121,6 +121,14 @@
     }
 
     /**
+     * Returns whether the display is a freeform display for which taskbar should be pinned
+     * and showing desktop tasks.
+     */
+    public boolean showDesktopTaskbarForFreeformDisplay(Context displayInfoContext) {
+        return false;
+    }
+
+    /**
      * Returns if the home is visible.
      */
     public boolean isHomeVisible(Context context) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
index 7e76e19..588a668 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -32,6 +32,7 @@
 import com.android.launcher3.dagger.LauncherAppComponent
 import com.android.launcher3.dagger.LauncherAppSingleton
 import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
+import com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE
 import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
 import com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener
@@ -229,6 +230,63 @@
             .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
         assertFalse(displayController.getInfo().isTransientTaskbar())
     }
+
+    @Test
+    @UiThreadTest
+    fun testTaskbarPinnedForDesktopTaskbar_inDesktopMode() {
+        whenever(windowManagerProxy.showDesktopTaskbarForFreeformDisplay(any())).thenReturn(true)
+        whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
+        whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(false)
+        whenever(windowManagerProxy.isInDesktopMode()).thenReturn(true)
+        whenever(windowManagerProxy.isHomeVisible(any())).thenReturn(false)
+        DisplayController.enableTaskbarModePreferenceForTests(true)
+
+        assertTrue(displayController.getInfo().isTransientTaskbar())
+
+        displayController.onConfigurationChanged(configuration)
+
+        verify(displayInfoChangeListener)
+            .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING or CHANGE_DESKTOP_MODE))
+        assertFalse(displayController.getInfo().isTransientTaskbar())
+    }
+
+    @Test
+    @UiThreadTest
+    fun testTaskbarPinnedForDesktopTaskbar_notInDesktopMode() {
+        whenever(windowManagerProxy.showDesktopTaskbarForFreeformDisplay(any())).thenReturn(true)
+        whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
+        whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(false)
+        whenever(windowManagerProxy.isInDesktopMode()).thenReturn(false)
+        whenever(windowManagerProxy.isHomeVisible(any())).thenReturn(false)
+        DisplayController.enableTaskbarModePreferenceForTests(true)
+
+        assertTrue(displayController.getInfo().isTransientTaskbar())
+
+        displayController.onConfigurationChanged(configuration)
+
+        verify(displayInfoChangeListener)
+            .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
+        assertFalse(displayController.getInfo().isTransientTaskbar())
+    }
+
+    @Test
+    @UiThreadTest
+    fun testTaskbarPinnedForDesktopTaskbar_onHome() {
+        whenever(windowManagerProxy.showDesktopTaskbarForFreeformDisplay(any())).thenReturn(true)
+        whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
+        whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(false)
+        whenever(windowManagerProxy.isInDesktopMode()).thenReturn(false)
+        whenever(windowManagerProxy.isHomeVisible(any())).thenReturn(true)
+        DisplayController.enableTaskbarModePreferenceForTests(true)
+
+        assertTrue(displayController.getInfo().isTransientTaskbar())
+
+        displayController.onConfigurationChanged(configuration)
+
+        verify(displayInfoChangeListener)
+            .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
+        assertFalse(displayController.getInfo().isTransientTaskbar())
+    }
 }
 
 class MyWmProxy : WindowManagerProxy()