Setup SearchResultIcon for single cell results

SearchResultIcon will be able to render apps, shortcuts and remote actions. It can also handle its own focused state drawing.

Screenshot: https://screenshot.googleplex.com/C3KgjJtLQTBPgaf

Bug: 170752716
Test: Manual
Change-Id: I460a9c128ea3f5814784e342c5d5fa5b7e310882
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 97e3786..a47a500 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -30,6 +30,7 @@
     with some minor changed based on the derivative app.
     -->
 
+    <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 79fb612..069954c 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2015 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,16 +12,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.BubbleTextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    style="@style/BaseIcon"
+    style="@style/BaseIcon.AllApps"
     android:id="@+id/icon"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
     launcher:iconDisplay="all_apps"
-    launcher:centerVertically="true"
-    android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
-    android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
+    launcher:centerVertically="true" />
 
diff --git a/res/layout/search_result_icon.xml b/res/layout/search_result_icon.xml
new file mode 100644
index 0000000..3c1dd49
--- /dev/null
+++ b/res/layout/search_result_icon.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.views.SearchResultIcon xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    style="@style/BaseIcon.AllApps"
+    launcher:iconDisplay="all_apps"
+    launcher:centerVertically="true" />
+
diff --git a/res/values/styles.xml b/res/values/styles.xml
index fd3d873..067cf7f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -223,6 +223,16 @@
         <item name="android:lines">1</item>
     </style>
 
+    <!-- Base theme for AllApps BubbleTextViews -->
+    <style name="BaseIcon.AllApps" parent="BaseIcon">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:stateListAnimator">@animator/all_apps_fastscroll_icon_anim</item>
+        <item name="android:paddingLeft">@dimen/dynamic_grid_cell_padding_x</item>
+        <item name="android:paddingRight">@dimen/dynamic_grid_cell_padding_x</item>
+    </style>
+
+
     <!-- Icon displayed on the workspace -->
     <style name="BaseIcon.Workspace" >
         <item name="android:shadowRadius">2.0</item>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 817d028..6245637 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.graphics.IconShape.getShape;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
@@ -27,15 +28,18 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.PointF;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Process;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -50,6 +54,8 @@
 
 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;
 import com.android.launcher3.folder.FolderIcon;
@@ -60,6 +66,7 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconCache.IconLoadRequest;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -79,7 +86,7 @@
  * too aggressive.
  */
 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
-        IconLabelDotView, DraggableView, Reorderable {
+        IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
@@ -87,6 +94,8 @@
     private static final int DISPLAY_HERO_APP = 5;
 
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
+    private static final float HIGHLIGHT_SCALE = 1.16f;
+
 
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@@ -95,6 +104,11 @@
 
     private float mScaleForReorderBounce = 1f;
 
+    protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Path mHighlightPath = new Path();
+    protected int mHighlightColor = Color.TRANSPARENT;
+    private final BlurMaskFilter mHighlightShadowFilter;
+
     private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
             = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
         @Override
@@ -208,6 +222,11 @@
         setEllipsize(TruncateAt.END);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
         setTextAlpha(1f);
+
+        int shadowSize = context.getResources().getDimensionPixelSize(
+                R.dimen.blur_size_click_shadow);
+        mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
+
     }
 
     @Override
@@ -421,8 +440,38 @@
 
     @Override
     public void onDraw(Canvas canvas) {
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
+            int count = canvas.save();
+            drawFocusHighlight(canvas);
+            canvas.restoreToCount(count);
+        }
         super.onDraw(canvas);
-        drawDotIfNecessary(canvas);
+    }
+
+    protected void drawFocusHighlight(Canvas canvas) {
+        boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
+                ((ItemInfo) getTag()).user);
+        float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
+        canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
+        float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
+        mHighlightPath.reset();
+        mHighlightPaint.reset();
+        getIconBounds(mDotParams.iconBounds);
+        getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
+                outlineSize / 2);
+        if (isBadged) {
+            float borderSize = outlineSize - getIconSize();
+            float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
+            float badgeInset = outlineSize - badgeSize;
+            getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
+                    mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
+        }
+        mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
+        mHighlightPaint.setColor(mDotParams.color);
+        canvas.drawPath(mHighlightPath, mHighlightPaint);
+        mHighlightPaint.setMaskFilter(null);
+        mHighlightPaint.setColor(mHighlightColor);
+        canvas.drawPath(mHighlightPath, mHighlightPaint);
     }
 
     /**
@@ -787,10 +836,11 @@
 
     @Override
     public SafeCloseable prepareDrawDragView() {
+        int highlightColor = mHighlightColor;
+        mHighlightColor = Color.TRANSPARENT;
         resetIconScale();
         setForceHideDot(true);
-        return () -> {
-        };
+        return () -> mHighlightColor = highlightColor;
     }
 
     private void resetIconScale() {
@@ -827,4 +877,17 @@
         });
         iconUpdateAnimation.start();
     }
+
+
+    @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 603e9df..a1bf7ed 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -43,15 +43,12 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
-import com.android.launcher3.allapps.search.SearchEventTracker;
 import com.android.launcher3.allapps.search.SearchSectionInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.views.HeroSearchResultView;
 import com.android.launcher3.views.SearchSliceWrapper;
 import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
 
 import java.util.List;
 
@@ -94,9 +91,11 @@
 
     public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
 
+    public static final int VIEW_TYPE_SEARCH_ICON = 1 << 14;
+
     // Common view type masks
     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
-    public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
+    public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_SEARCH_ICON;
 
     /**
      * ViewHolder for each icon.
@@ -186,6 +185,7 @@
                     || viewType == VIEW_TYPE_SEARCH_PEOPLE
                     || viewType == VIEW_TYPE_SEARCH_THUMBNAIL
                     || viewType == VIEW_TYPE_SEARCH_ICON_ROW
+                    || viewType == VIEW_TYPE_SEARCH_ICON
                     || viewType == VIEW_TYPE_SEARCH_SUGGEST;
         }
     }
@@ -399,11 +399,8 @@
                         R.layout.all_apps_icon, parent, false);
                 icon.setLongPressTimeoutFactor(1f);
                 icon.setOnFocusChangeListener(mIconFocusListener);
-                if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-                    icon.setOnClickListener(mOnIconClickListener);
-                    icon.setOnLongClickListener(mOnIconLongClickListener);
-                }
-
+                icon.setOnClickListener(mOnIconClickListener);
+                icon.setOnLongClickListener(mOnIconLongClickListener);
                 // Ensure the all apps icon height matches the workspace icons in portrait mode.
                 icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
                 return new ViewHolder(icon);
@@ -419,6 +416,9 @@
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.all_apps_divider, parent, false));
+            case VIEW_TYPE_SEARCH_ICON:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.search_result_icon, parent, false));
             case VIEW_TYPE_SEARCH_CORPUS_TITLE:
                 return new ViewHolder(
                         mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
@@ -453,6 +453,10 @@
 
     @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);
@@ -460,43 +464,6 @@
                 BubbleTextView icon = (BubbleTextView) holder.itemView;
                 icon.reset();
                 icon.applyFromApplicationInfo(info);
-                if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-                    break;
-                }
-                //TODO: replace with custom TopHitBubbleTextView with support for both shortcut
-                // and apps
-                if (adapterItem instanceof SearchAdapterItem) {
-                    SearchAdapterItem item = (SearchAdapterItem) adapterItem;
-                    SearchTargetHandler searchTargetHandler = new SearchTargetHandler() {
-                        @Override
-                        public void applySearchTarget(SearchTarget searchTarget) {
-                            // Does nothing
-                        }
-
-                        @Override
-                        public void handleSelection(int type) {
-                            SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
-                                    type, item.position, item.getSearchSessionId());
-                            e.bundle = HeroSearchResultView.getAppBundle(info);
-                            SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(e);
-                        }
-                    };
-                    SearchEventTracker.INSTANCE.get(mLauncher).registerWeakHandler(
-                            ((SearchAdapterItem) adapterItem).getSearchTarget(),
-                            searchTargetHandler);
-
-                    icon.setOnClickListener(view -> {
-                        searchTargetHandler.handleSelection(SearchTargetEvent.SELECT);
-                        mOnIconClickListener.onClick(view);
-                    });
-                    icon.setOnLongClickListener(view -> {
-                        searchTargetHandler.handleSelection(SearchTargetEvent.LONG_PRESS);
-                        return mOnIconLongClickListener.onLongClick(view);
-                    });
-                } else {
-                    icon.setOnClickListener(mOnIconClickListener);
-                    icon.setOnLongClickListener(mOnIconLongClickListener);
-                }
                 break;
             case VIEW_TYPE_EMPTY_SEARCH:
                 TextView emptyViewText = (TextView) holder.itemView;
@@ -525,6 +492,7 @@
             case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
             case VIEW_TYPE_SEARCH_HERO_APP:
             case VIEW_TYPE_SEARCH_ROW:
+            case VIEW_TYPE_SEARCH_ICON:
             case VIEW_TYPE_SEARCH_ICON_ROW:
             case VIEW_TYPE_SEARCH_PEOPLE:
             case VIEW_TYPE_SEARCH_THUMBNAIL:
@@ -544,11 +512,10 @@
     public void onViewRecycled(@NonNull ViewHolder holder) {
         super.onViewRecycled(holder);
         if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
-        if (holder.itemView instanceof BubbleTextView) {
-            BubbleTextView icon = (BubbleTextView) holder.itemView;
-            icon.setOnClickListener(null);
-            icon.setOnLongClickListener(null);
-        } else if (holder.itemView instanceof SliceView) {
+        if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
+            ((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
+        }
+        if (holder.itemView instanceof SliceView) {
             SliceView sliceView = (SliceView) holder.itemView;
             if (sliceView.getTag() instanceof SearchSliceWrapper) {
                 ((SearchSliceWrapper) sliceView.getTag()).destroy();
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
index c131697..3c81811 100644
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
@@ -54,6 +54,9 @@
         int i = 0;
         while (i < itemCount) {
             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) {
@@ -93,7 +96,7 @@
             int index = mAppsView.getApps().getFocusedChildIndex();
             AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
                     mAppsView.getActiveRecyclerView().getLayoutManager();
-            if (layoutManager.findFirstVisibleItemPosition() == index
+            if (layoutManager.findFirstVisibleItemPosition() <= index
                     && index < parent.getChildCount()) {
                 decorationHandler.onFocusDraw(c, parent.getChildAt(index));
             }
@@ -156,6 +159,10 @@
             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);
@@ -169,4 +176,18 @@
         }
     }
 
+    /**
+     * 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/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 3ae0a18..50fb60a 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -18,6 +18,8 @@
 import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
 
+import android.content.ComponentName;
+import android.os.UserHandle;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -27,6 +29,7 @@
 import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.systemui.plugins.shared.SearchTarget;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -86,6 +89,19 @@
         return index < 0 ? null : mApps[index];
     }
 
+    /**
+     * Gets AppInfo from {@link SearchTarget}
+     * TODO: SearchTarget should have userHandle and ComponentKey members
+     */
+    public AppInfo getAppFromSearchTarget(SearchTarget searchTarget) {
+        ComponentName cn = searchTarget.bundle.getParcelable("component_name");
+        UserHandle userHandle = searchTarget.bundle.getParcelable("user_handle");
+        if (cn == null) {
+            throw new IllegalStateException("Component name is required for AppInfo");
+        }
+        return getApp(new ComponentKey(cn, userHandle));
+    }
+
     public void enableDeferUpdates(int flag) {
         mDeferUpdatesFlags |= flag;
     }
diff --git a/src/com/android/launcher3/allapps/search/SearchEventTracker.java b/src/com/android/launcher3/allapps/search/SearchEventTracker.java
index 6bcde6c..c276434 100644
--- a/src/com/android/launcher3/allapps/search/SearchEventTracker.java
+++ b/src/com/android/launcher3/allapps/search/SearchEventTracker.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
 import android.content.Context;
 
 import androidx.annotation.Nullable;
@@ -61,7 +63,7 @@
      */
     public void notifySearchTargetEvent(SearchTargetEvent searchTargetEvent) {
         if (mPlugin != null) {
-            mPlugin.notifySearchTargetEvent(searchTargetEvent);
+            UI_HELPER_EXECUTOR.post(() -> mPlugin.notifySearchTargetEvent(searchTargetEvent));
         }
     }
 
diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java
index 9e56e00..e2ba93b 100644
--- a/src/com/android/launcher3/views/HeroSearchResultView.java
+++ b/src/com/android/launcher3/views/HeroSearchResultView.java
@@ -19,13 +19,11 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Point;
 import android.os.Bundle;
-import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.view.View;
@@ -51,7 +49,6 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
 import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.util.ComponentKey;
 import com.android.systemui.plugins.shared.SearchTarget;
 import com.android.systemui.plugins.shared.SearchTargetEvent;
 
@@ -122,7 +119,8 @@
 
     @Override
     public void applySearchTarget(SearchTarget searchTarget) {
-        AppInfo appInfo = getAppInfo(searchTarget.bundle);
+        AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore();
+        AppInfo appInfo = apps.getAppFromSearchTarget(searchTarget);
 //        TODO: replace this with searchTarget.shortcuts
         ArrayList<ShortcutInfo> infos = searchTarget.bundle.getParcelableArrayList(
                 SHORTCUTS_KEY);
@@ -156,13 +154,6 @@
         SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
     }
 
-    private AppInfo getAppInfo(Bundle bundle) {
-        AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore();
-        ComponentName cn = bundle.getParcelable("component_name");
-        UserHandle userHandle = bundle.getParcelable("user_handle");
-        return (cn != null) ? apps.getApp(new ComponentKey(cn, userHandle)) : null;
-    }
-
     @Override
     public Object[] getTargetInfo() {
         return mTargetInfo;
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 15ff2f5..77cec80 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -42,7 +42,7 @@
  */
 public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener {
 
-    private static final float SCRIM_ALPHA = .8f;
+    private static final float SCRIM_ALPHA = .75f;
     protected final T mLauncher;
     private final WallpaperColorInfo mWallpaperColorInfo;
     protected final int mEndScrim;
diff --git a/src/com/android/launcher3/views/SearchResultIcon.java b/src/com/android/launcher3/views/SearchResultIcon.java
new file mode 100644
index 0000000..e0f9ab7
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultIcon.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A {@link BubbleTextView} representing a single cell result in AllApps
+ */
+public class SearchResultIcon extends BubbleTextView implements
+        AllAppsSearchBarController.SearchTargetHandler, View.OnClickListener,
+        View.OnLongClickListener {
+    private final Object[] mTargetInfo = createTargetInfo();
+    private final Launcher mLauncher;
+
+    private SearchTarget mSearchTarget;
+
+    public SearchResultIcon(Context context) {
+        this(context, null, 0);
+    }
+
+    public SearchResultIcon(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SearchResultIcon(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mLauncher = Launcher.getLauncher(getContext());
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setLongPressTimeoutFactor(1f);
+        setOnFocusChangeListener(mLauncher.getFocusHandler());
+        setOnClickListener(this);
+        setOnLongClickListener(this);
+        getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+    }
+
+    @Override
+    public void applySearchTarget(SearchTarget searchTarget) {
+        mSearchTarget = searchTarget;
+        AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
+        if (searchTarget.type == SearchTarget.ItemType.APP) {
+            AppInfo appInfo = appsStore.getAppFromSearchTarget(searchTarget);
+            applyFromApplicationInfo(appInfo);
+        }
+    }
+
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
+    @Override
+    public void handleSelection(int eventType) {
+        SearchTargetEvent event = getSearchTargetEvent(mSearchTarget.type,
+                eventType);
+        if (mSearchTarget.type.equals(SearchTarget.ItemType.APP)) {
+            event.bundle = HeroSearchResultView.getAppBundle((ItemInfo) getTag());
+        }
+        SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event);
+    }
+
+    @Override
+    public void onClick(View view) {
+        handleSelection(SearchTargetEvent.SELECT);
+        mLauncher.getItemOnClickListener().onClick(view);
+    }
+
+    @Override
+    public boolean onLongClick(View view) {
+        handleSelection(SearchTargetEvent.LONG_PRESS);
+        return ItemLongClickListener.INSTANCE_ALL_APPS.onLongClick(view);
+    }
+}
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
index ddeefaf..5115f69 100644
--- a/src/com/android/launcher3/views/SearchResultIconRow.java
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
@@ -44,6 +45,7 @@
 import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.Themes;
 import com.android.systemui.plugins.shared.SearchTarget;
 import com.android.systemui.plugins.shared.SearchTarget.ItemType;
 import com.android.systemui.plugins.shared.SearchTargetEvent;
@@ -98,6 +100,14 @@
     }
 
     @Override
+    protected void drawFocusHighlight(Canvas canvas) {
+        mHighlightPaint.setColor(mHighlightColor);
+        float r = Themes.getDialogCornerRadius(getContext());
+        canvas.drawRoundRect(0, 0, getWidth(), getHeight(), r, r, mHighlightPaint);
+    }
+
+
+    @Override
     public void applySearchTarget(SearchTarget searchTarget) {
         if (searchTarget.mRemoteAction != null) {
             prepareUsingRemoteAction(searchTarget.mRemoteAction,