Do not reload launcher when changing active display

> Updating IDP to use fixed bitmap sizes, so that the cache
  stays valid
> Caching last known windowMetrices for non-active displays and
  using estimation only when the cache is not available
> Only reloading model if IDP change could have affected the model
> Remove unnecessary listeners from IDP which are already controlled by
  model lifecycle

Bug: 191657065
Test: Manual
Change-Id: Ia8e6dfafd0977e62aa3fcf367ad79f7a49b2df51
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 2e14823..a2189c9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -57,6 +57,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -158,38 +159,6 @@
     @VisibleForTesting
     public InvariantDeviceProfile() {}
 
-    private InvariantDeviceProfile(InvariantDeviceProfile p) {
-        numRows = p.numRows;
-        numColumns = p.numColumns;
-        numFolderRows = p.numFolderRows;
-        numFolderColumns = p.numFolderColumns;
-        iconSize = p.iconSize;
-        landscapeIconSize = p.landscapeIconSize;
-        twoPanelPortraitIconSize = p.twoPanelPortraitIconSize;
-        twoPanelLandscapeIconSize = p.twoPanelLandscapeIconSize;
-        iconBitmapSize = p.iconBitmapSize;
-        iconTextSize = p.iconTextSize;
-        landscapeIconTextSize = p.landscapeIconTextSize;
-        twoPanelPortraitIconTextSize = p.twoPanelPortraitIconTextSize;
-        twoPanelLandscapeIconTextSize = p.twoPanelLandscapeIconTextSize;
-        numShownHotseatIcons = p.numShownHotseatIcons;
-        numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
-        numAllAppsColumns = p.numAllAppsColumns;
-        numDatabaseAllAppsColumns = p.numDatabaseAllAppsColumns;
-        isScalable = p.isScalable;
-        devicePaddingId = p.devicePaddingId;
-        minCellHeight = p.minCellHeight;
-        minCellWidth = p.minCellWidth;
-        borderSpacing = p.borderSpacing;
-        dbFile = p.dbFile;
-        allAppsIconSize = p.allAppsIconSize;
-        allAppsIconTextSize = p.allAppsIconTextSize;
-        defaultLayoutId = p.defaultLayoutId;
-        demoModeLayoutId = p.demoModeLayoutId;
-        mExtraAttrs = p.mExtraAttrs;
-        devicePaddings = p.devicePaddings;
-    }
-
     @TargetApi(23)
     private InvariantDeviceProfile(Context context) {
         String gridName = getCurrentGridName(context);
@@ -236,13 +205,13 @@
 
         DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
                 .add(myDisplayOption);
-        result.iconSize = defaultDisplayOption.iconSize;
-        result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
-        if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
-            result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
-        } else {
-            result.allAppsIconSize = myDisplayOption.allAppsIconSize;
+        result.iconSizes[DisplayOption.INDEX_DEFAULT] =
+                defaultDisplayOption.iconSizes[DisplayOption.INDEX_DEFAULT];
+        for (int i = 1; i < DisplayOption.COUNT_TOTAL; i++) {
+            result.iconSizes[i] = Math.min(
+                    defaultDisplayOption.iconSizes[i], myDisplayOption.iconSizes[i]);
         }
+
         result.minCellHeight = defaultDisplayOption.minCellHeight;
         result.minCellWidth = defaultDisplayOption.minCellWidth;
         result.borderSpacing = defaultDisplayOption.borderSpacing;
@@ -288,17 +257,21 @@
 
         mExtraAttrs = closestProfile.extraAttrs;
 
-        iconSize = displayOption.iconSize;
-        landscapeIconSize = displayOption.landscapeIconSize;
-        twoPanelPortraitIconSize = displayOption.twoPanelPortraitIconSize;
-        twoPanelLandscapeIconSize = displayOption.twoPanelLandscapeIconSize;
+        iconSize = displayOption.iconSizes[DisplayOption.INDEX_DEFAULT];
+        landscapeIconSize = displayOption.iconSizes[DisplayOption.INDEX_LANDSCAPE];
+        twoPanelPortraitIconSize = displayOption.iconSizes[DisplayOption.INDEX_TWO_PANEL_PORTRAIT];
+        twoPanelLandscapeIconSize =
+                displayOption.iconSizes[DisplayOption.INDEX_TWO_PANEL_LANDSCAPE];
         iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
-        iconTextSize = displayOption.iconTextSize;
-        landscapeIconTextSize = displayOption.landscapeIconTextSize;
-        twoPanelPortraitIconTextSize = displayOption.twoPanelPortraitIconTextSize;
-        twoPanelLandscapeIconTextSize = displayOption.twoPanelLandscapeIconTextSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
+        iconTextSize = displayOption.textSizes[DisplayOption.INDEX_DEFAULT];
+        landscapeIconTextSize = displayOption.textSizes[DisplayOption.INDEX_LANDSCAPE];
+        twoPanelPortraitIconTextSize =
+                displayOption.textSizes[DisplayOption.INDEX_TWO_PANEL_PORTRAIT];
+        twoPanelLandscapeIconTextSize =
+                displayOption.textSizes[DisplayOption.INDEX_TWO_PANEL_LANDSCAPE];
+
         minCellHeight = displayOption.minCellHeight;
         minCellWidth = displayOption.minCellWidth;
         borderSpacing = displayOption.borderSpacing;
@@ -312,8 +285,8 @@
                 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
         if (Utilities.isGridOptionsEnabled(context)) {
-            allAppsIconSize = displayOption.allAppsIconSize;
-            allAppsIconTextSize = displayOption.allAppsIconTextSize;
+            allAppsIconSize = displayOption.iconSizes[DisplayOption.INDEX_ALL_APPS];
+            allAppsIconTextSize = displayOption.textSizes[DisplayOption.INDEX_ALL_APPS];
         } else {
             allAppsIconSize = iconSize;
             allAppsIconTextSize = iconTextSize;
@@ -374,13 +347,22 @@
         MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext));
     }
 
+    private Object[] toModelState() {
+        return new Object[] {
+                numColumns, numRows, numDatabaseHotseatIcons, iconBitmapSize, fillResIconDpi,
+                numDatabaseAllAppsColumns, dbFile};
+    }
+
     private void onConfigChanged(Context context) {
+        Object[] oldState = toModelState();
+
         // Re-init grid
         String gridName = getCurrentGridName(context);
         initGrid(context, gridName);
 
+        boolean modelPropsChanged = !Arrays.equals(oldState, toModelState());
         for (OnIDPChangeListener listener : mChangeListeners) {
-            listener.onIdpChanged(this);
+            listener.onIdpChanged(modelPropsChanged);
         }
     }
 
@@ -533,22 +515,33 @@
                 Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
                         dist(width, height, b.minWidthDps, b.minHeightDps)));
 
-        GridOption closestOption = points.get(0).grid;
+        DisplayOption closestPoint = points.get(0);
+        GridOption closestOption = closestPoint.grid;
         float weights = 0;
 
-        DisplayOption p = points.get(0);
-        if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
-            return p;
+        if (dist(width, height, closestPoint.minWidthDps, closestPoint.minHeightDps) == 0) {
+            return closestPoint;
         }
 
         DisplayOption out = new DisplayOption(closestOption);
         for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
-            p = points.get(i);
+            DisplayOption p = points.get(i);
             float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
             weights += w;
             out.add(new DisplayOption().add(p).multiply(w));
         }
-        return out.multiply(1.0f / weights);
+        out.multiply(1.0f / weights);
+
+        // Since the bitmaps are persisted, ensure that the default bitmap size is same as
+        // predefined size to avoid cache invalidation
+        out.iconSizes[DisplayOption.INDEX_DEFAULT] =
+                closestPoint.iconSizes[DisplayOption.INDEX_DEFAULT];
+        for (int i = DisplayOption.INDEX_DEFAULT + 1; i < DisplayOption.COUNT_TOTAL; i++) {
+            out.iconSizes[i] = Math.min(out.iconSizes[i],
+                    out.iconSizes[DisplayOption.INDEX_DEFAULT]);
+        }
+
+        return out;
     }
 
     public DeviceProfile getDeviceProfile(Context context) {
@@ -614,7 +607,7 @@
         /**
          * Called when the device provide changes
          */
-        void onIdpChanged(InvariantDeviceProfile profile);
+        void onIdpChanged(boolean modelPropertiesChanged);
     }
 
 
@@ -695,6 +688,14 @@
     @VisibleForTesting
     static final class DisplayOption {
 
+        static final int INDEX_DEFAULT = 0;
+        static final int INDEX_LANDSCAPE = 1;
+        static final int INDEX_ALL_APPS = 2;
+        static final int INDEX_TWO_PANEL_PORTRAIT = 3;
+        static final int INDEX_TWO_PANEL_LANDSCAPE = 4;
+
+        static final int COUNT_TOTAL = 5;
+
         public final GridOption grid;
 
         private final float minWidthDps;
@@ -705,16 +706,8 @@
         private float minCellWidth;
         private float borderSpacing;
 
-        private float iconSize;
-        private float iconTextSize;
-        private float landscapeIconSize;
-        private float twoPanelPortraitIconSize;
-        private float twoPanelLandscapeIconSize;
-        private float landscapeIconTextSize;
-        private float twoPanelPortraitIconTextSize;
-        private float twoPanelLandscapeIconTextSize;
-        private float allAppsIconSize;
-        private float allAppsIconTextSize;
+        private final float[] iconSizes = new float[COUNT_TOTAL];
+        private final float[] textSizes = new float[COUNT_TOTAL];
 
         DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) {
             this.grid = grid;
@@ -732,27 +725,36 @@
             minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
             borderSpacing = a.getFloat(R.styleable.ProfileDisplayOption_borderSpacingDps, 0);
 
-            iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
-            landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
-                    iconSize);
-            twoPanelPortraitIconSize = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitIconSize, iconSize);
-            twoPanelLandscapeIconSize = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeIconSize,
-                    landscapeIconSize);
-            iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
-            landscapeIconTextSize = a.getFloat(
-                    R.styleable.ProfileDisplayOption_landscapeIconTextSize, iconTextSize);
-            twoPanelPortraitIconTextSize = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitIconTextSize, iconTextSize);
-            twoPanelLandscapeIconTextSize = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeIconTextSize,
-                    landscapeIconTextSize);
+            iconSizes[INDEX_DEFAULT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
+            iconSizes[INDEX_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+            iconSizes[INDEX_ALL_APPS] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+            iconSizes[INDEX_TWO_PANEL_PORTRAIT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconSize,
+                            iconSizes[INDEX_DEFAULT]);
+            iconSizes[INDEX_TWO_PANEL_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconSize,
+                            iconSizes[INDEX_LANDSCAPE]);
 
-            allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
-                    iconSize);
-            allAppsIconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
-                    iconTextSize);
+            textSizes[INDEX_DEFAULT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
+            textSizes[INDEX_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+            textSizes[INDEX_ALL_APPS] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+            textSizes[INDEX_TWO_PANEL_PORTRAIT] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconTextSize,
+                            textSizes[INDEX_DEFAULT]);
+            textSizes[INDEX_TWO_PANEL_LANDSCAPE] =
+                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconTextSize,
+                            textSizes[INDEX_LANDSCAPE]);
+
             a.recycle();
         }
 
@@ -771,16 +773,10 @@
         }
 
         private DisplayOption multiply(float w) {
-            iconSize *= w;
-            landscapeIconSize *= w;
-            twoPanelPortraitIconSize *= w;
-            twoPanelLandscapeIconSize *= w;
-            allAppsIconSize *= w;
-            iconTextSize *= w;
-            landscapeIconTextSize *= w;
-            twoPanelPortraitIconTextSize *= w;
-            twoPanelLandscapeIconTextSize *= w;
-            allAppsIconTextSize *= w;
+            for (int i = 0; i < COUNT_TOTAL; i++) {
+                iconSizes[i] *= w;
+                textSizes[i] *= w;
+            }
             minCellHeight *= w;
             minCellWidth *= w;
             borderSpacing *= w;
@@ -788,16 +784,10 @@
         }
 
         private DisplayOption add(DisplayOption p) {
-            iconSize += p.iconSize;
-            landscapeIconSize += p.landscapeIconSize;
-            twoPanelPortraitIconSize += p.twoPanelPortraitIconSize;
-            twoPanelLandscapeIconSize += p.twoPanelLandscapeIconSize;
-            allAppsIconSize += p.allAppsIconSize;
-            iconTextSize += p.iconTextSize;
-            landscapeIconTextSize += p.landscapeIconTextSize;
-            twoPanelPortraitIconTextSize += p.twoPanelPortraitIconTextSize;
-            twoPanelLandscapeIconTextSize += p.twoPanelLandscapeIconTextSize;
-            allAppsIconTextSize += p.allAppsIconTextSize;
+            for (int i = 0; i < COUNT_TOTAL; i++) {
+                iconSizes[i] += p.iconSizes[i];
+                textSizes[i] += p.textSizes[i];
+            }
             minCellHeight += p.minCellHeight;
             minCellWidth += p.minCellWidth;
             borderSpacing += p.borderSpacing;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6ea7b17..3754dc1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -558,7 +558,7 @@
     public void onConfigurationChanged(Configuration newConfig) {
         int diff = newConfig.diff(mOldConfig);
         if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
-            onIdpChanged(mDeviceProfile.inv);
+            onIdpChanged(false);
         }
 
         mOldConfig.setTo(newConfig);
@@ -566,8 +566,8 @@
     }
 
     @Override
-    public void onIdpChanged(InvariantDeviceProfile idp) {
-        initDeviceProfile(idp);
+    public void onIdpChanged(boolean modelPropertiesChanged) {
+        initDeviceProfile(mDeviceProfile.inv);
         dispatchDeviceProfileChanged();
         reapplyUi();
         mDragLayer.recreateControllers();
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 702b73a..8adbcd9 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -83,7 +83,11 @@
         Log.v(Launcher.TAG, "LauncherAppState initiated");
         Preconditions.assertUIThread();
 
-        mInvariantDeviceProfile.addOnChangeListener(idp -> refreshAndReloadLauncher());
+        mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> {
+            if (modelPropertiesChanged) {
+                refreshAndReloadLauncher();
+            }
+        });
 
         mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
 
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index e2c0a32..7446181 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -23,6 +23,8 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
+import static java.util.Collections.emptyMap;
+
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.ComponentCallbacks;
@@ -34,10 +36,11 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Build;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.Display;
-import android.view.WindowMetrics;
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
@@ -47,7 +50,7 @@
 import com.android.launcher3.uioverrides.ApiWrapper;
 
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -76,8 +79,8 @@
 
     // Null for SDK < S
     private final Context mWindowContext;
-
     private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
+
     private Info mInfo;
 
     private DisplayController(Context context) {
@@ -95,19 +98,24 @@
             mContext.registerReceiver(configChangeReceiver,
                     new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
         }
+        mInfo = new Info(getDisplayInfoContext(display), display,
+                getInternalDisplays(mDM), emptyMap());
+        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+    }
 
-        // Create a single holder for all internal displays. External display holders created
-        // lazily.
-        Set<PortraitSize> extraInternalDisplays = new ArraySet<>();
-        for (Display d : mDM.getDisplays()) {
-            if (ApiWrapper.isInternalDisplay(display) && d.getDisplayId() != DEFAULT_DISPLAY) {
+    private static ArrayMap<String, PortraitSize> getInternalDisplays(
+            DisplayManager displayManager) {
+        Display[] displays = displayManager.getDisplays();
+        ArrayMap<String, PortraitSize> internalDisplays = new ArrayMap<>();
+        for (Display display : displays) {
+            if (ApiWrapper.isInternalDisplay(display)) {
                 Point size = new Point();
-                d.getRealSize(size);
-                extraInternalDisplays.add(new PortraitSize(size.x, size.y));
+                display.getRealSize(size);
+                internalDisplays.put(ApiWrapper.getUniqueId(display),
+                        new PortraitSize(size.x, size.y));
             }
         }
-        mInfo = new Info(getDisplayInfoContext(display), display, extraInternalDisplays);
-        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+        return internalDisplays;
     }
 
     @Override
@@ -203,11 +211,16 @@
     @AnyThread
     private void handleInfoChange(Display display) {
         Info oldInfo = mInfo;
-        Set<PortraitSize> extraDisplaysSizes = oldInfo.mAllSizes.size() > 1
-                ? oldInfo.mAllSizes : Collections.emptySet();
 
         Context displayContext = getDisplayInfoContext(display);
-        Info newInfo = new Info(displayContext, display, extraDisplaysSizes);
+        Info newInfo = new Info(displayContext, display,
+                oldInfo.mInternalDisplays, oldInfo.mPerDisplayBounds);
+
+        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
+            // Cache may not be valid anymore, recreate without cache
+            newInfo = new Info(displayContext, display, getInternalDisplays(mDM), emptyMap());
+        }
+
         int change = 0;
         if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) {
             change |= CHANGE_ACTIVE_SCREEN;
@@ -240,7 +253,6 @@
 
     public static class Info {
 
-        public final int id;
         public final int singleFrameMs;
 
         // Configuration properties
@@ -249,19 +261,21 @@
         public final int densityDpi;
 
         private final PortraitSize mScreenSizeDp;
-        private final Set<PortraitSize> mAllSizes;
 
         public final Point currentSize;
 
         public final Set<WindowBounds> supportedBounds = new ArraySet<>();
+        private final Map<String, Set<WindowBounds>> mPerDisplayBounds = new ArrayMap<>();
+        private final ArrayMap<String, PortraitSize> mInternalDisplays;
 
         public Info(Context context, Display display) {
-            this(context, display, Collections.emptySet());
+            this(context, display, new ArrayMap<>(), emptyMap());
         }
 
-        private Info(Context context, Display display, Set<PortraitSize> extraDisplaysSizes) {
-            id = display.getDisplayId();
-
+        private Info(Context context, Display display,
+                ArrayMap<String, PortraitSize> internalDisplays,
+                Map<String, Set<WindowBounds>> perDisplayBoundsCache) {
+            mInternalDisplays = internalDisplays;
             rotation = display.getRotation();
 
             Configuration config = context.getResources().getConfiguration();
@@ -271,32 +285,51 @@
 
             singleFrameMs = getSingleFrameMs(display);
             currentSize = new Point();
-
             display.getRealSize(currentSize);
 
-            if (extraDisplaysSizes.isEmpty() || !Utilities.ATLEAST_S) {
-                Point smallestSize = new Point();
-                Point largestSize = new Point();
-                display.getCurrentSizeRange(smallestSize, largestSize);
+            String myDisplayId = ApiWrapper.getUniqueId(display);
+            Set<WindowBounds> currentSupportedBounds =
+                    getSupportedBoundsForDisplay(display, currentSize);
+            mPerDisplayBounds.put(myDisplayId, currentSupportedBounds);
+            supportedBounds.addAll(currentSupportedBounds);
 
-                int portraitWidth = Math.min(currentSize.x, currentSize.y);
-                int portraitHeight = Math.max(currentSize.x, currentSize.y);
+            if (ApiWrapper.isInternalDisplay(display) && internalDisplays.size() > 1) {
+                int displayCount = internalDisplays.size();
+                for (int i = 0; i < displayCount; i++) {
+                    String displayKey = internalDisplays.keyAt(i);
+                    if (TextUtils.equals(myDisplayId, displayKey)) {
+                        continue;
+                    }
 
-                supportedBounds.add(new WindowBounds(portraitWidth, portraitHeight,
-                        smallestSize.x, largestSize.y));
-                supportedBounds.add(new WindowBounds(portraitHeight, portraitWidth,
-                        largestSize.x, smallestSize.y));
-                mAllSizes = Collections.singleton(new PortraitSize(currentSize.x, currentSize.y));
-            } else {
-                mAllSizes = new ArraySet<>(extraDisplaysSizes);
-                mAllSizes.add(new PortraitSize(currentSize.x, currentSize.y));
-                Set<WindowMetrics> metrics = WindowManagerCompat.getDisplayProfiles(
-                        context, mAllSizes, densityDpi,
-                        ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
-                metrics.forEach(wm -> supportedBounds.add(WindowBounds.fromWindowMetrics(wm)));
+                    Set<WindowBounds> displayBounds = perDisplayBoundsCache.get(displayKey);
+                    if (displayBounds == null) {
+                        // We assume densityDpi is the same across all internal displays
+                        displayBounds = WindowManagerCompat.estimateDisplayProfiles(
+                                context, internalDisplays.valueAt(i), densityDpi,
+                                ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
+                    }
+
+                    supportedBounds.addAll(displayBounds);
+                    mPerDisplayBounds.put(displayKey, displayBounds);
+                }
             }
         }
 
+        private static Set<WindowBounds> getSupportedBoundsForDisplay(Display display, Point size) {
+            Point smallestSize = new Point();
+            Point largestSize = new Point();
+            display.getCurrentSizeRange(smallestSize, largestSize);
+
+            int portraitWidth = Math.min(size.x, size.y);
+            int portraitHeight = Math.max(size.x, size.y);
+            Set<WindowBounds> result = new ArraySet<>();
+            result.add(new WindowBounds(portraitWidth, portraitHeight,
+                    smallestSize.x, largestSize.y));
+            result.add(new WindowBounds(portraitHeight, portraitWidth,
+                    largestSize.x, smallestSize.y));
+            return result;
+        }
+
         /**
          * Returns true if the bounds represent a tablet
          */
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
index 38a63de..bfdf1e4 100644
--- a/src/com/android/launcher3/util/WindowManagerCompat.java
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -24,6 +24,7 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Build;
+import android.util.ArraySet;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
@@ -31,14 +32,14 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController.PortraitSize;
 
-import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Set;
 
 /**
- * Utility class to simulate window manager APIs until proper APIs are available
+ * Utility class to estimate window manager values
  */
 @TargetApi(Build.VERSION_CODES.S)
 public class WindowManagerCompat {
@@ -46,51 +47,51 @@
     public static final int MIN_TABLET_WIDTH = 600;
 
     /**
-     * Returns a set of supported render sizes for a set of internal displays.
-     * This is a temporary workaround which assumes only nav-bar insets change across displays
+     * Returns a set of supported render sizes for a internal display.
+     * This is a temporary workaround which assumes only nav-bar insets change across displays, and
+     * is only used until we eventually get the real values
      * @param consumeTaskBar if true, it assumes that task bar is part of the app window
      *                       and ignores any insets because of task bar.
      */
-    public static Set<WindowMetrics> getDisplayProfiles(
-            Context windowContext, Collection<PortraitSize> allDisplaySizes,
-            int densityDpi, boolean consumeTaskBar) {
-        WindowInsets metrics = windowContext.getSystemService(WindowManager.class)
+    public static Set<WindowBounds> estimateDisplayProfiles(
+            Context windowContext, PortraitSize size, int densityDpi, boolean consumeTaskBar) {
+        if (!Utilities.ATLEAST_S) {
+            return Collections.emptySet();
+        }
+        WindowInsets defaultInsets = windowContext.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics().getWindowInsets();
         boolean hasNavbar = ResourceUtils.getIntegerByName(
                 "config_navBarInteractionMode",
                 windowContext.getResources(),
                 INVALID_RESOURCE_HANDLE) != 0;
 
-        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics);
+        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(defaultInsets);
+        Set<WindowBounds> result = new ArraySet<>();
+        int swDP = (int) dpiFromPx(size.width, densityDpi);
+        boolean isTablet = swDP >= MIN_TABLET_WIDTH;
 
-        Set<WindowMetrics> result = new HashSet<>();
-        for (PortraitSize size : allDisplaySizes) {
-            int swDP = (int) dpiFromPx(size.width, densityDpi);
-            boolean isTablet = swDP >= MIN_TABLET_WIDTH;
-
-            final Insets portraitNav, landscapeNav;
-            if (isTablet && !consumeTaskBar) {
-                portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
-                        .getDimensionPixelSize(R.dimen.taskbar_size));
-            } else if (hasNavbar) {
-                portraitNav = Insets.of(0, 0, 0,
-                        getSystemResource(windowContext, "navigation_bar_height", swDP));
-                landscapeNav = isTablet
-                        ? Insets.of(0, 0, 0, getSystemResource(windowContext,
-                                "navigation_bar_height_landscape", swDP))
-                        : Insets.of(0, 0, getSystemResource(windowContext,
-                                "navigation_bar_width", swDP), 0);
-            } else {
-                portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
-            }
-
-            result.add(new WindowMetrics(
-                    new Rect(0, 0, size.width, size.height),
-                    insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build()));
-            result.add(new WindowMetrics(
-                    new Rect(0, 0, size.height, size.width),
-                    insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build()));
+        final Insets portraitNav, landscapeNav;
+        if (isTablet && !consumeTaskBar) {
+            portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
+                    .getDimensionPixelSize(R.dimen.taskbar_size));
+        } else if (hasNavbar) {
+            portraitNav = Insets.of(0, 0, 0,
+                    getSystemResource(windowContext, "navigation_bar_height", swDP));
+            landscapeNav = isTablet
+                    ? Insets.of(0, 0, 0, getSystemResource(windowContext,
+                            "navigation_bar_height_landscape", swDP))
+                    : Insets.of(0, 0, getSystemResource(windowContext,
+                            "navigation_bar_width", swDP), 0);
+        } else {
+            portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
         }
+
+        result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
+                new Rect(0, 0, size.width, size.height),
+                insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build())));
+        result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
+                new Rect(0, 0, size.height, size.width),
+                insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build())));
         return result;
     }