Add group highlighting to AA+ result

Bug: 180071886
Test: manual
Change-Id: Id1cc3d7400e1a7fe41af272d689aa9315030b420
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index e5a4335..ee4d7ec 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -53,7 +53,6 @@
 
 import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.allapps.AllAppsSectionDecorator;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DraggableView;
@@ -84,7 +83,7 @@
  * too aggressive.
  */
 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
-        IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
+        IconLabelDotView, DraggableView, Reorderable {
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
@@ -954,16 +953,4 @@
             setCompoundDrawables(null, newIcon, null, null);
         }
     }
-
-    @Override
-    public void decorate(int color) {
-        mHighlightColor = color;
-        invalidate();
-    }
-
-    @Override
-    public void removeDecoration() {
-        mHighlightColor = Color.TRANSPARENT;
-        invalidate();
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 715c142..5030c5e 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -42,8 +42,7 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchSectionInfo;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.allapps.search.SectionDecorationInfo;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -109,7 +108,7 @@
         // The index of this app not including sections
         public int appIndex = -1;
         // Search section associated to result
-        public SearchSectionInfo searchSectionInfo = null;
+        public SectionDecorationInfo sectionDecorationInfo = null;
 
         /**
          * Factory method for AppIcon AdapterItem
@@ -372,10 +371,6 @@
 
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
-        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()
-                && holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
-            ((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
-        }
         switch (holder.getItemViewType()) {
             case VIEW_TYPE_ICON:
                 AdapterItem adapterItem = mApps.getAdapterItems().get(position);
@@ -409,10 +404,6 @@
     @Override
     public void onViewRecycled(@NonNull ViewHolder holder) {
         super.onViewRecycled(holder);
-        if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
-        if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
-            ((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
-        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
index 6c95992..176dce6 100644
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
@@ -18,16 +18,18 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.RectF;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
-import com.android.launcher3.allapps.search.SearchSectionInfo;
+import com.android.launcher3.allapps.search.SectionDecorationInfo;
 import com.android.launcher3.util.Themes;
 
 import java.util.List;
@@ -45,52 +47,51 @@
 
     @Override
     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        // Iterate through views in recylerview and draw bounds around views in the same section.
-        // Since views in the same section will follow each other, we can skip to a last view in
-        // a section to get the bounds of the section without having to iterate on every item.
-        int itemCount = parent.getChildCount();
         List<AllAppsGridAdapter.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
-        SectionDecorationHandler lastDecorationHandler = null;
-        int i = 0;
-        while (i < itemCount) {
+        boolean drawFallbackFocusedView = true;
+        for (int i = 0; i < parent.getChildCount(); i++) {
             View view = parent.getChildAt(i);
-            if (view instanceof SelfDecoratingView) {
-                ((SelfDecoratingView) view).removeDecoration();
-            }
             int position = parent.getChildAdapterPosition(view);
             AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
-            if (adapterItem.searchSectionInfo != null) {
-                SearchSectionInfo sectionInfo = adapterItem.searchSectionInfo;
-                int endIndex = Math.min(i + sectionInfo.getPosEnd() - position, itemCount - 1);
+            if (adapterItem.sectionDecorationInfo != null) {
+                SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo;
                 SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler();
-                if (decorationHandler != lastDecorationHandler && lastDecorationHandler != null) {
-                    drawDecoration(c, lastDecorationHandler, parent);
-                }
-                lastDecorationHandler = decorationHandler;
                 if (decorationHandler != null) {
                     decorationHandler.extendBounds(view);
-                }
-
-                if (endIndex > i) {
-                    i = endIndex;
-                    continue;
+                    if (sectionInfo.isFocusedView()) {
+                        decorationHandler.onFocusDraw(c, view);
+                        drawFallbackFocusedView = false;
+                    } else {
+                        decorationHandler.onGroupDraw(c);
+                    }
                 }
             }
-            i++;
         }
-        if (lastDecorationHandler != null) {
-            drawDecoration(c, lastDecorationHandler, parent);
+        // fallback logic in case none of the SearchTarget is labeled as focused item
+        if (drawFallbackFocusedView) {
+            for (int i = 0; i < parent.getChildCount(); i++) {
+                View view = parent.getChildAt(i);
+                int position = parent.getChildAdapterPosition(view);
+                AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
+                if (adapterItem.sectionDecorationInfo != null) {
+                    SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo;
+                    SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler();
+                    if (decorationHandler != null) {
+                        drawDecoration(c, decorationHandler, parent);
+                    }
+                }
+            }
         }
     }
 
-    private void drawDecoration(Canvas c, SectionDecorationHandler decorationHandler,
-            RecyclerView parent) {
-        if (decorationHandler == null) return;
+    // Fallback logic in case non of the SearchTarget is labeled as focused item.
+    private void drawDecoration(@NonNull Canvas c,
+            @NonNull SectionDecorationHandler decorationHandler,
+            @NonNull RecyclerView parent) {
         if (decorationHandler.mIsFullWidth) {
             decorationHandler.mBounds.left = parent.getPaddingLeft();
             decorationHandler.mBounds.right = parent.getWidth() - parent.getPaddingRight();
         }
-        decorationHandler.onDraw(c);
         if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
                 && mAppsView.getApps().getFocusedChild() != null) {
             int index = mAppsView.getApps().getFocusedChildIndex();
@@ -109,23 +110,41 @@
      * Handles grouping and drawing of items in the same all apps sections.
      */
     public static class SectionDecorationHandler {
-        private static final int FILL_ALPHA = 0;
-
         protected RectF mBounds = new RectF();
         private final boolean mIsFullWidth;
         private final float mRadius;
 
-        protected int mFocusColor;
-        protected int mFillcolor;
+        protected final int mFocusColor; // main focused item color
+        protected final int mFillcolor; // grouping color
+
         private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        private final boolean mIsTopRound;
+        private final boolean mIsBottomRound;
+        private float [] mCorners;
+        private float mFillSpacing;
 
+        public SectionDecorationHandler(Context context, boolean isFullWidth, int fillAlpha,
+                boolean isTopRound, boolean isBottomRound) {
 
-        public SectionDecorationHandler(Context context, boolean isFullWidth) {
             mIsFullWidth = isFullWidth;
             int endScrim = Themes.getColorBackground(context);
-            mFillcolor = ColorUtils.setAlphaComponent(endScrim, FILL_ALPHA);
-            mFocusColor = endScrim;
-            mRadius = Themes.getDialogCornerRadius(context);
+            mFillcolor = ColorUtils.setAlphaComponent(endScrim, fillAlpha);
+            mFocusColor = ColorUtils.setAlphaComponent(endScrim, fillAlpha);
+
+            mIsTopRound = isTopRound;
+            mIsBottomRound = isBottomRound;
+
+            mRadius = context.getResources().getDimensionPixelSize(
+                    R.dimen.search_decoration_corner_radius);
+            mFillSpacing = context.getResources().getDimensionPixelSize(
+                    R.dimen.search_decoration_padding);
+            mCorners = new float[]{
+                    mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top left radius in px
+                    mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top right radius in px
+                    mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0, // Bottom right
+                    mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0  // Bottom left
+            };
+
         }
 
         /**
@@ -147,9 +166,9 @@
         /**
          * Draw bounds onto canvas.
          */
-        public void onDraw(Canvas canvas) {
+        public void onGroupDraw(Canvas canvas) {
             mPaint.setColor(mFillcolor);
-            canvas.drawRoundRect(mBounds, mRadius, mRadius, mPaint);
+            onDraw(canvas);
         }
 
         /**
@@ -159,13 +178,20 @@
             if (view == null) {
                 return;
             }
-            if (view instanceof SelfDecoratingView) {
-                ((SelfDecoratingView) view).decorate(mFocusColor);
-                return;
-            }
             mPaint.setColor(mFocusColor);
-            canvas.drawRoundRect(view.getLeft(), view.getTop(),
-                    view.getRight(), view.getBottom(), mRadius, mRadius, mPaint);
+            mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+            onDraw(canvas);
+        }
+
+
+        private void onDraw(Canvas canvas) {
+            final Path path = new Path();
+            RectF finalBounds = new RectF(mBounds.left + mFillSpacing,
+                    mBounds.top + mFillSpacing,
+                    mBounds.right - mFillSpacing,
+                    mBounds.bottom - mFillSpacing);
+            path.addRoundRect(finalBounds, mCorners, Path.Direction.CW);
+            canvas.drawPath(path, mPaint);
         }
 
         /**
@@ -175,19 +201,4 @@
             mBounds.setEmpty();
         }
     }
-
-    /**
-     * An interface for a view to draw highlight indicator
-     */
-    public interface SelfDecoratingView {
-        /**
-         * Removes decorations drawing if focus is acquired by another view
-         */
-        void removeDecoration();
-
-        /**
-         * Draws highlight indicator on view.
-         */
-        void decorate(int focusColor);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 1dc10fe..123ace7 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -20,7 +20,7 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
-import com.android.launcher3.allapps.search.SearchSectionInfo;
+import com.android.launcher3.allapps.search.SectionDecorationInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -201,20 +201,11 @@
     }
 
     void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
-        SearchSectionInfo lastSection = null;
         for (int i = 0; i < list.size(); i++) {
             AdapterItem adapterItem = list.get(i);
             adapterItem.position = offset + i;
             mAdapterItems.add(adapterItem);
-            if (adapterItem.searchSectionInfo != lastSection) {
-                if (adapterItem.searchSectionInfo != null) {
-                    adapterItem.searchSectionInfo.setPosStart(adapterItem.position);
-                }
-                if (lastSection != null) {
-                    lastSection.setPosEnd(adapterItem.position - 1);
-                }
-                lastSection = adapterItem.searchSectionInfo;
-            }
+
             if (adapterItem.isCountedForAccessibility()) {
                 mAccessibilityResultsCount++;
             }
@@ -295,16 +286,16 @@
         mFastScrollerSections.clear();
         mAdapterItems.clear();
 
-        SearchSectionInfo appSection = new SearchSectionInfo();
+        SectionDecorationInfo appSection = new SectionDecorationInfo();
         appSection.setDecorationHandler(
-                new AllAppsSectionDecorator.SectionDecorationHandler(mLauncher, true));
+                new AllAppsSectionDecorator.SectionDecorationHandler(mLauncher, true,
+                        0, false, false));
 
         // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
         // ordered set of sections
 
         if (!hasFilter()) {
             mAccessibilityResultsCount = mApps.size();
-            appSection.setPosStart(position);
             for (AppInfo info : mApps) {
                 String sectionName = info.sectionName;
 
@@ -321,11 +312,10 @@
                     lastFastScrollerSectionInfo.fastScrollToItem = appItem;
                 }
                 if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-                    appItem.searchSectionInfo = appSection;
+                    appItem.sectionDecorationInfo = appSection;
                 }
                 mAdapterItems.add(appItem);
             }
-            appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
         } else {
             updateSearchAdapterItems(mSearchResults, 0);
             if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
index 84688e1..f9fb22e 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
@@ -37,14 +37,14 @@
 
     private static final int MAX_RESULTS_COUNT = 5;
 
-    private final SearchSectionInfo mSearchSectionInfo;
+    private final SectionDecorationInfo mSearchSectionInfo;
     private final LauncherAppState mLauncherAppState;
 
     public AppsSearchPipeline(Context context, LauncherAppState launcherAppState) {
         mLauncherAppState = launcherAppState;
-        mSearchSectionInfo = new SearchSectionInfo();
+        mSearchSectionInfo = new SectionDecorationInfo();
         mSearchSectionInfo.setDecorationHandler(
-                new SectionDecorationHandler(context, true));
+                new SectionDecorationHandler(context, true, 0, true, true));
     }
 
     @Override
@@ -81,7 +81,7 @@
         ArrayList<AdapterItem> items = new ArrayList<>();
         for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) {
             AdapterItem appItem = AdapterItem.asApp(i, "", matchingApps.get(i), i);
-            appItem.searchSectionInfo = mSearchSectionInfo;
+            appItem.sectionDecorationInfo = mSearchSectionInfo;
             items.add(appItem);
         }
 
diff --git a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
similarity index 73%
rename from src/com/android/launcher3/allapps/search/SearchSectionInfo.java
rename to src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
index 464df68..0b64fca 100644
--- a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
+++ b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
@@ -18,37 +18,30 @@
 import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
 
 /**
- * Info class for a search section
+ * Info class for a search section that is primarily used for decoration.
  */
-public class SearchSectionInfo {
+public class SectionDecorationInfo {
+
+    public static final int QUICK_LAUNCH = 1 << 0;
+    public static final int GROUPING = 1 << 1;
 
     private String mSectionId;
+    private boolean mFocused;
     private SectionDecorationHandler mDecorationHandler;
 
-    public int getPosStart() {
-        return mPosStart;
+    public boolean isFocusedView() {
+        return mFocused;
     }
 
-    public void setPosStart(int posStart) {
-        mPosStart = posStart;
+    public void setFocusedView(boolean focused) {
+        mFocused = focused;
     }
 
-    public int getPosEnd() {
-        return mPosEnd;
-    }
-
-    public void setPosEnd(int posEnd) {
-        mPosEnd = posEnd;
-    }
-
-    private int mPosStart;
-    private int mPosEnd;
-
-    public SearchSectionInfo() {
+    public SectionDecorationInfo() {
         this(null);
     }
 
-    public SearchSectionInfo(String sectionId) {
+    public SectionDecorationInfo(String sectionId) {
         mSectionId = sectionId;
     }
 
@@ -56,7 +49,6 @@
         mDecorationHandler = sectionDecorationHandler;
     }
 
-
     public SectionDecorationHandler getDecorationHandler() {
         return mDecorationHandler;
     }