Taskbar overflow: only show if apps don't fit + limit # of icons

- Only shows the overflow icon if all of the recent apps do
  not fit in the taskbar.
- Limits the number of recent app icons on screen so that they
  do not go out of bounds.
- Removes the overflow if it's not needed.

Bug: 368119679
Test: open app, enter desktop windowing, overflow only shows
      when reaching max number of apps
Flag: com.android.launcher3.taskbar_overflow
Change-Id: I96040495ee38f67bce8e41f9eb4ec17fcef5189a
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 2734137..8763509 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -120,6 +120,8 @@
 
     private boolean mShouldTryStartAlign;
 
+    private final int mMaxNumIcons;
+
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -185,6 +187,18 @@
         }
         // TODO: Disable touch events on QSB otherwise it can crash.
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+
+        mMaxNumIcons = calculateMaxNumIcons();
+    }
+
+    /**
+     // @return the maximum number of 'icons' that can fit in the taskbar.
+     // TODO(368119679): Assumes that they are all the same size.
+     */
+    private int calculateMaxNumIcons() {
+        int availableWidth = mActivityContext.getDeviceProfile().widthPx
+                - (mActivityContext.getDeviceProfile().edgeMarginPx * 2);
+        return Math.floorDiv(availableWidth, mIconTouchSize);
     }
 
     @Override
@@ -473,6 +487,9 @@
                 addView(mTaskbarDividerContainer, mIsRtl ? (getChildCount() - 1) : 1);
             }
         }
+
+        updateRecentAppsToFit();
+
         if (mActivityContext.getDeviceProfile().isQsbInline) {
             addView(mQsb, mIsRtl ? getChildCount() : 0);
             // Always set QSB to invisible after re-adding.
@@ -480,6 +497,45 @@
         }
     }
 
+    /**
+     * Updates the recent apps portion of the taskbar by:
+     * - Removing overflow affordance if overflow is not needed.
+     * - Removing any recent apps that do not fit.
+     */
+    private void updateRecentAppsToFit() {
+        if (!Flags.taskbarOverflow()) {
+            return;
+        }
+        int indexOfFirstRecentApp = -1;
+        int size = getChildCount();
+        boolean removeOverflowView = true;
+
+        for (int i = 0; i < size; ++i) {
+            if (getChildAt(i).getTag() instanceof GroupTask) {
+                indexOfFirstRecentApp = i;
+                removeOverflowView = false;
+                break;
+            }
+        }
+
+        if (indexOfFirstRecentApp != -1) {
+            // We pre-maturely added the overflow icon, so we can take it out of the count.
+            int numRecentAppsToRemove = Math.max(0, getChildCount() - mMaxNumIcons + 1);
+            if (numRecentAppsToRemove <= 1) {
+                // We can fit all of the recent apps if we remove the overflow icon.
+                removeOverflowView = true;
+            } else {
+                for (int i = 0; i < numRecentAppsToRemove; ++i) {
+                    removeAndRecycle(getChildAt(indexOfFirstRecentApp));
+                }
+            }
+        }
+
+        if (removeOverflowView) {
+            removeView(mTaskbarOverflowView);
+        }
+    }
+
     /** Binds the GroupTask to the BubbleTextView to be ready to present to the user. */
     public void applyGroupTaskToBubbleTextView(BubbleTextView btv, GroupTask groupTask) {
         // TODO(b/343289567): support app pairs.