More robust fix to BubbleTextViews appearing on multiple rows.

This is the more comprehensive version of ag/20119299.
Improvements compared to that change:
 - Icons on the same row animate together (as opposed to the
   first icon on the row animating separately)
 - Multiple rows of predicted apps are supported (any beyond
   the first row are animated like everything else)

--- Original description ---

Example BubbleTextView: WhatsApp/Gmail conversation shortcuts.

The issue was we were treating these the same as the top app
row, but we were assuming there would only be 1 such row, which
messed up the measurement logic. At the same time, the logic
specific to that app row was redundant with the new logic for
rows containing mulitple items.

This solution does 2 things:
 - Removes special logic for app row (it now uses the same
   logic as other rows with multiple items, i.e. uses the span
   index to determine the height of the row)
 - Keeps the scale/alpha at 1 for the first row of app icons.
   This currently only applies to predicted apps, but if there
   were multiple app rows in the future, the additional rows
   would animate the same way as other rows (see demo videos
   for an example with double predicted apps in 0 state). In
   the conversation case, the other icons are deep shortcuts.

The result is the app row still does what it did before (stays
fixed at full size/opacity), and deep shortcuts like the ones
used for WhatsApp and Gmail animate like other rows of items,
such as screenshots.

Demo videos: https://drive.google.com/drive/folders/1GPQNIwMfuj9ZdAbRrh-K75C5xJYT4Gzo?resourcekey=0-ojO6VGetEBy5YTq4roFmlw&usp=sharing

Test: Manual with and without inject_web_top (which moves app row)
for WhatsApp and Gmail with AiAi fishfood.
Bug: 239927522

Change-Id: Ib2ca97b93798cb57eb55545eeba8be9322484f7d
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index bde4fa6..9c3dab4 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -170,12 +170,13 @@
     /**
      * Updates the children views of SearchRecyclerView based on the current animation progress.
      *
-     * @return the total height of animating views (excluding any app icons).
+     * @return the total height of animating views (excluding at most one row of app icons).
      */
     private int updateSearchRecyclerViewProgress() {
         int numSearchResultsAnimated = 0;
         int totalHeight = 0;
         int appRowHeight = 0;
+        boolean appRowComplete = false;
         Integer top = null;
         SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
 
@@ -189,67 +190,72 @@
                 top = searchResultView.getTop();
             }
 
-            if (searchResultView instanceof BubbleTextView
-                    && searchResultView.getTag() instanceof ItemInfo
-                    && ((ItemInfo) searchResultView.getTag()).itemType == ITEM_TYPE_APPLICATION) {
-                // The first app icon will set appRowHeight, which will also contribute to
-                // totalHeight. Additional app icons should remove the appRowHeight to remain in
-                // the same row as the first app.
-                searchResultView.setY(top + totalHeight - appRowHeight);
-                if (appRowHeight == 0) {
-                    appRowHeight = searchResultView.getHeight();
-                    totalHeight += appRowHeight;
-                }
-                // Don't scale/fade app row.
-                searchResultView.setScaleY(1);
-                searchResultView.setAlpha(1);
-                continue;
-            }
-
-            // Adjust content alpha based on start progress and stagger.
-            float startContentFadeProgress = Math.max(0,
-                    TOP_CONTENT_FADE_PROGRESS_START - CONTENT_STAGGER * numSearchResultsAnimated);
-            float endContentFadeProgress = Math.min(1,
-                    startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
-            searchResultView.setAlpha(1 - clampToProgress(mSearchToAzProgress,
-                    startContentFadeProgress, endContentFadeProgress));
-
-            // Adjust background (or decorator) alpha based on start progress and stagger.
-            float startBackgroundFadeProgress = Math.max(0,
-                    TOP_BACKGROUND_FADE_PROGRESS_START
-                            - CONTENT_STAGGER * numSearchResultsAnimated);
-            float endBackgroundFadeProgress = Math.min(1,
-                    startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
-            float backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
-                    startBackgroundFadeProgress, endBackgroundFadeProgress);
             int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView);
-            boolean decoratorFilled =
-                    adapterPosition != NO_POSITION
-                            && searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
-                            .setDecorationFillAlpha((int) (255 * backgroundAlpha));
-            if (!decoratorFilled) {
-                // Try to adjust background alpha instead (e.g. for Search Edu card).
-                Drawable background = searchResultView.getBackground();
-                if (background != null) {
-                    background.setAlpha((int) (255 * backgroundAlpha));
+            int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
+            appRowComplete |= appRowHeight > 0 && spanIndex == 0;
+            // We don't animate the first (currently only) app row we see, as that is assumed to be
+            // predicted/prefix-matched apps.
+            boolean shouldAnimate = !isAppIcon(searchResultView) || appRowComplete;
+
+            float contentAlpha = 1f;
+            float backgroundAlpha = 1f;
+            if (shouldAnimate) {
+                if (spanIndex > 0) {
+                    // Animate this item with the previous item on the same row.
+                    numSearchResultsAnimated--;
                 }
+
+                // Adjust content alpha based on start progress and stagger.
+                float startContentFadeProgress = Math.max(0,
+                        TOP_CONTENT_FADE_PROGRESS_START
+                                - CONTENT_STAGGER * numSearchResultsAnimated);
+                float endContentFadeProgress = Math.min(1,
+                        startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
+                contentAlpha = 1 - clampToProgress(mSearchToAzProgress,
+                        startContentFadeProgress, endContentFadeProgress);
+
+                // Adjust background (or decorator) alpha based on start progress and stagger.
+                float startBackgroundFadeProgress = Math.max(0,
+                        TOP_BACKGROUND_FADE_PROGRESS_START
+                                - CONTENT_STAGGER * numSearchResultsAnimated);
+                float endBackgroundFadeProgress = Math.min(1,
+                        startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
+                backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
+                        startBackgroundFadeProgress, endBackgroundFadeProgress);
+
+                numSearchResultsAnimated++;
+            }
+            searchResultView.setAlpha(contentAlpha);
+            // Apply background alpha to decorator if possible.
+            if (adapterPosition != NO_POSITION) {
+                searchRecyclerView.getApps().getAdapterItems()
+                        .get(adapterPosition).setDecorationFillAlpha((int) (255 * backgroundAlpha));
+            }
+            // Apply background alpha to view's background (e.g. for Search Edu card).
+            Drawable background = searchResultView.getBackground();
+            if (background != null) {
+                background.setAlpha((int) (255 * backgroundAlpha));
             }
 
-            float scaleY = 1 - mSearchToAzProgress;
+            float scaleY = 1;
+            if (shouldAnimate) {
+                scaleY = 1 - mSearchToAzProgress;
+            }
             int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
             searchResultView.setScaleY(scaleY);
 
             // For rows with multiple elements, only count the height once and translate elements to
             // the same y position.
             int y = top + totalHeight;
-            int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
             if (spanIndex > 0) {
                 // Continuation of an existing row; move this item into the row.
                 y -= scaledHeight;
             } else {
-                // Start of a new row contributes to total height and animation stagger.
-                numSearchResultsAnimated++;
+                // Start of a new row contributes to total height.
                 totalHeight += scaledHeight;
+                if (!shouldAnimate) {
+                    appRowHeight = scaledHeight;
+                }
             }
             searchResultView.setY(y);
         }
@@ -275,6 +281,11 @@
         return adapter.getSpanIndex(adapterPosition);
     }
 
+    private boolean isAppIcon(View item) {
+        return item instanceof BubbleTextView && item.getTag() instanceof ItemInfo
+                && ((ItemInfo) item.getTag()).itemType == ITEM_TYPE_APPLICATION;
+    }
+
     /** Called just before a child is attached to the SearchRecyclerView. */
     private void onSearchChildAttached(View child) {
         // Avoid allocating hardware layers for alpha changes.