Merge "Refactor OverviewComponentObserver for connected displays" into main
diff --git a/quickstep/src/com/android/quickstep/DisplayModel.kt b/quickstep/src/com/android/quickstep/DisplayModel.kt
index 27a3379..ac94375 100644
--- a/quickstep/src/com/android/quickstep/DisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/DisplayModel.kt
@@ -35,7 +35,7 @@
     }
 
     private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
-    private val displayResourceArray = SparseArray<RESOURCE_TYPE>()
+    protected val displayResourceArray = SparseArray<RESOURCE_TYPE>()
 
     private val displayListener: DisplayManager.DisplayListener =
         (object : DisplayManager.DisplayListener {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 699c5df..74aa8e2 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -196,7 +196,7 @@
         mDisplayId = displayId;
         mHomeIntent = componentObserver.getHomeIntent();
         mOverviewIntent = componentObserver.getOverviewIntent();
-        mContainerInterface = componentObserver.getContainerInterface();
+        mContainerInterface = componentObserver.getContainerInterface(displayId);
         mStateCallback = new MultiStateCallback(
                 STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
         mGestureId = gestureId;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index fff85f6..6bd3400 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -22,6 +22,7 @@
 import android.os.SystemClock
 import android.os.Trace
 import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
 import android.view.View
 import android.window.TransitionInfo
 import androidx.annotation.BinderThread
@@ -91,9 +92,11 @@
      */
     private var keyboardTaskFocusIndex = -1
 
+    // TODO (b/397942185): get per-display interface
     private val containerInterface: BaseContainerInterface<*, *>
-        get() = overviewComponentObserver.containerInterface
+        get() = overviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY)
 
+    // TODO (b/397942185): get per-display RecentsView
     private val visibleRecentsView: RecentsView<*, *>?
         get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
 
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index bc3de41..7eacef3 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -21,6 +21,7 @@
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
@@ -87,7 +88,7 @@
             new CopyOnWriteArrayList<>();
 
     private String mUpdateRegisteredPackage;
-    private BaseContainerInterface mContainerInterface;
+    private BaseContainerInterface mDefaultDisplayContainerInterface;
     private Intent mOverviewIntent;
     private boolean mIsHomeAndOverviewSame;
     private boolean mIsDefaultHome;
@@ -175,8 +176,8 @@
         // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
         // launcher made invisible become visible again before the new activity control helper
         // becomes active.
-        if (mContainerInterface != null) {
-            mContainerInterface.onAssistantVisibilityChanged(0.f);
+        if (mDefaultDisplayContainerInterface != null) {
+            mDefaultDisplayContainerInterface.onAssistantVisibilityChanged(0.f);
         }
 
         if (SEPARATE_RECENTS_ACTIVITY.get() || Flags.enableLauncherOverviewInWindow()) {
@@ -193,7 +194,7 @@
 
         if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
             // User default home is same as out home app. Use Overview integrated in Launcher.
-            mContainerInterface = LauncherActivityInterface.INSTANCE;
+            mDefaultDisplayContainerInterface = LauncherActivityInterface.INSTANCE;
             mIsHomeAndOverviewSame = true;
             mOverviewIntent = mMyHomeIntent;
             mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -202,12 +203,11 @@
             unregisterOtherHomeAppUpdateReceiver();
         } else {
             // The default home app is a different launcher. Use the fallback Overview instead.
-
             if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
-                mContainerInterface =
+                mDefaultDisplayContainerInterface =
                         mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
             } else {
-                mContainerInterface = FallbackActivityInterface.INSTANCE;
+                mDefaultDisplayContainerInterface = FallbackActivityInterface.INSTANCE;
             }
             mIsHomeAndOverviewSame = false;
             mOverviewIntent = mFallbackIntent;
@@ -296,12 +296,16 @@
     }
 
     /**
-     * Get the current control helper for managing interactions to the overview container.
+     * Get the current control helper for managing interactions to the overview container for
+     * the given displayId.
      *
-     * @return the current control helper
+     * @param displayId The display id
+     * @return the control helper for the given display
      */
-    public BaseContainerInterface<?,?> getContainerInterface() {
-        return mContainerInterface;
+    public BaseContainerInterface<?, ?> getContainerInterface(int displayId) {
+        return (enableOverviewOnConnectedDisplays() && displayId != DEFAULT_DISPLAY)
+                ? mRecentsDisplayModel.getFallbackWindowInterface(displayId)
+                : mDefaultDisplayContainerInterface;
     }
 
     public void dump(PrintWriter pw) {
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index f47937c..e9f7024 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,5 +1,7 @@
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -210,8 +212,9 @@
     }
 
     private RecentsViewContainer getRecentsViewContainer() {
+        // TODO (b/400647896): support per-display container in e2e tests
         return OverviewComponentObserver.INSTANCE.get(mContext)
-                .getContainerInterface().getCreatedContainer();
+                .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 8bc8549..ba662c4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
@@ -92,6 +93,7 @@
 import com.android.quickstep.OverviewCommandHelper.CommandType;
 import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
 import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
 import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -127,6 +129,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.util.Locale;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -299,8 +302,9 @@
         @Override
         public void enterStageSplitFromRunningApp(boolean leftOrTop) {
             executeForTouchInteractionService(tis -> {
+                // TODO (b/397942185): support external displays
                 RecentsViewContainer container = tis.mOverviewComponentObserver
-                        .getContainerInterface().getCreatedContainer();
+                        .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
                 if (container != null) {
                     container.enterStageSplitFromRunningApp(leftOrTop);
                 }
@@ -575,6 +579,8 @@
 
     private DisplayController.DisplayInfoChangeListener mDisplayInfoChangeListener;
 
+    private RecentsDisplayModel mRecentsDisplayModel;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -585,6 +591,7 @@
         mMainChoreographer = Choreographer.getInstance();
         mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this);
         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
+        mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this);
         mAllAppsActionManager = new AllAppsActionManager(
                 this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
         mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
@@ -597,7 +604,7 @@
         });
 
         mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks,
-                RecentsDisplayModel.getINSTANCE().get(this));
+                mRecentsDisplayModel);
         mDesktopAppLaunchTransitionManager =
                 new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
         mDesktopAppLaunchTransitionManager.registerTransitions();
@@ -682,8 +689,7 @@
         mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState);
         mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
         mOverviewCommandHelper = new OverviewCommandHelper(this,
-                mOverviewComponentObserver, mTaskAnimationManager,
-                RecentsDisplayModel.getINSTANCE().get(this),
+                mOverviewComponentObserver, mTaskAnimationManager, mRecentsDisplayModel,
                 SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager);
         mResetGestureInputConsumer = new ResetGestureInputConsumer(
                 mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
@@ -728,8 +734,11 @@
 
     private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
         mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
+        // TODO (b/399089118): how will this work with per-display Taskbars? Is using the
+        //  default-display container ok?
         RecentsViewContainer newOverviewContainer =
-                mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
+                mOverviewComponentObserver.getContainerInterface(
+                        DEFAULT_DISPLAY).getCreatedContainer();
         if (newOverviewContainer != null) {
             if (newOverviewContainer instanceof StatefulActivity activity) {
                 // This will also call setRecentsViewContainer() internally.
@@ -771,7 +780,8 @@
     @UiThread
     private void onAssistantVisibilityChanged() {
         if (LockedUserState.get(this).isUserUnlocked()) {
-            mOverviewComponentObserver.getContainerInterface().onAssistantVisibilityChanged(
+            mOverviewComponentObserver.getContainerInterface(
+                    DEFAULT_DISPLAY).onAssistantVisibilityChanged(
                     mDeviceState.getAssistantVisibility());
         }
     }
@@ -1128,8 +1138,10 @@
         if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
+        // TODO (b/399094853): handle config updates for all connected displays (relevant only for
+        // gestures on external displays)
         final BaseContainerInterface containerInterface =
-                mOverviewComponentObserver.getContainerInterface();
+                mOverviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY);
         final RecentsViewContainer container = containerInterface.getCreatedContainer();
         if (container == null || container.isStarted()) {
             // We only care about the existing background activity.
@@ -1182,22 +1194,27 @@
             mInputMonitorDisplayModel.dump("\t", pw);
         }
         DisplayController.INSTANCE.get(this).dump(pw);
-        pw.println("TouchState:");
-        RecentsViewContainer createdOverviewContainer = mOverviewComponentObserver == null ? null
-                : mOverviewComponentObserver.getContainerInterface().getCreatedContainer();
-        boolean resumed = mOverviewComponentObserver != null
-                && mOverviewComponentObserver.getContainerInterface().isResumed();
-        pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
-        pw.println("\tresumed=" + resumed);
+        for (RecentsDisplayResource resource : mRecentsDisplayModel.getActiveDisplayResources()) {
+            int displayId = resource.getDisplayId();
+            pw.println(String.format(Locale.ENGLISH, "TouchState (displayId %d):", displayId));
+            RecentsViewContainer createdOverviewContainer =
+                    mOverviewComponentObserver == null ? null
+                            : mOverviewComponentObserver.getContainerInterface(
+                                    displayId).getCreatedContainer();
+            boolean resumed = mOverviewComponentObserver != null
+                    && mOverviewComponentObserver.getContainerInterface(displayId).isResumed();
+            pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
+            pw.println("\tresumed=" + resumed);
+            if (createdOverviewContainer != null) {
+                createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
+            }
+        }
         pw.println("\tmConsumer=" + mConsumer.getName());
         ActiveGestureLog.INSTANCE.dump("", pw);
         RecentsModel.INSTANCE.get(this).dump("", pw);
         if (mTaskAnimationManager != null) {
             mTaskAnimationManager.dump("", pw);
         }
-        if (createdOverviewContainer != null) {
-            createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
-        }
         mTaskbarManager.dumpLogs("", pw);
         DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw);
         pw.println("ContextualSearchStateManager:");
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index 116b8f2..58c6c50 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.view.Display
+import androidx.core.util.valueIterator
 import com.android.launcher3.Flags
 import com.android.launcher3.dagger.ApplicationContext
 import com.android.launcher3.dagger.LauncherAppSingleton
@@ -78,6 +79,12 @@
         return getDisplayResource(displayId)?.fallbackWindowInterface
     }
 
+    val activeDisplayResources: Iterable<RecentsDisplayResource>
+        get() =
+            object : Iterable<RecentsDisplayResource> {
+                override fun iterator() = displayResourceArray.valueIterator()
+            }
+
     data class RecentsDisplayResource(
         val displayId: Int,
         val displayContext: Context,
diff --git a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
index 47b39db..df26b6b 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
+++ b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Trace
+import android.view.Display.DEFAULT_DISPLAY
 import com.android.launcher3.provider.RestoreDbTask
 import com.android.launcher3.util.Executors
 import com.android.launcher3.util.LockedUserState
@@ -59,7 +60,12 @@
             // The activity has been created before the initialization of overview service. It is
             // usually happens when booting or launcher is the top activity, so we should already
             // have the latest state.
-            if (fromInit && overviewCompObserver.containerInterface.createdContainer != null) return
+            if (
+                fromInit &&
+                    overviewCompObserver.getContainerInterface(DEFAULT_DISPLAY).createdContainer !=
+                        null
+            )
+                return
 
             ActiveGestureProtoLogProxy.logPreloadRecentsAnimation()
             val overviewIntent = Intent(overviewCompObserver.overviewIntentIgnoreSysUiState)
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index d00a39c..3bc9adc 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -21,6 +21,7 @@
 import android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH
 import android.content.Context
 import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
 import androidx.annotation.VisibleForTesting
 import com.android.internal.app.AssistUtils
 import com.android.launcher3.logging.StatsLogManager
@@ -222,7 +223,8 @@
 
     @VisibleForTesting
     fun getRecentsContainerInterface(): BaseContainerInterface<*, *>? {
-        return OverviewComponentObserver.INSTANCE.get(context).containerInterface
+        return OverviewComponentObserver.INSTANCE.get(context)
+            .getContainerInterface(DEFAULT_DISPLAY)
     }
 
     /**