Revert "Revert "Merge commit '8d14dbe041199d611839140f1c9285fd4174e9f4' ...""

This reverts commit 087a9e39b663dade90d8dfd74300e42575649b87.

Change-Id: I05b17a6e4aaaef6de3c2fa26aad45a39eb2833f9
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 310c306..5e50e27 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -37,8 +37,6 @@
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
@@ -82,7 +80,6 @@
             new ArrayList<>();
 
     protected DeviceProfile mDeviceProfile;
-    protected UserEventDispatcher mUserEventDispatcher;
     protected StatsLogManager mStatsLogManager;
     protected SystemUiController mSystemUiController;
 
@@ -144,8 +141,6 @@
         return mDeviceProfile;
     }
 
-    public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {}
-
     public final StatsLogManager getStatsLogManager() {
         if (mStatsLogManager == null) {
             mStatsLogManager = StatsLogManager.newInstance(this);
@@ -153,13 +148,6 @@
         return mStatsLogManager;
     }
 
-    public final UserEventDispatcher getUserEventDispatcher() {
-        if (mUserEventDispatcher == null) {
-            mUserEventDispatcher = UserEventDispatcher.newInstance(this);
-        }
-        return mUserEventDispatcher;
-    }
-
     public SystemUiController getSystemUiController() {
         if (mSystemUiController == null) {
             mSystemUiController = new SystemUiController(getWindow());
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 06bb263..1e5a9e4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -23,6 +23,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
@@ -30,6 +31,8 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 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;
@@ -43,6 +46,8 @@
 import android.view.ViewDebug;
 import android.widget.TextView;
 
+import androidx.core.graphics.ColorUtils;
+
 import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dot.DotInfo;
@@ -50,6 +55,7 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.PlaceHolderIconDrawable;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconCache.IconLoadRequest;
@@ -59,6 +65,7 @@
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.ActivityContext;
@@ -84,6 +91,8 @@
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
 
+    private static final int ICON_UPDATE_ANIMATION_DURATION = 375;
+
     private float mScaleForReorderBounce = 1f;
 
     private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
@@ -292,6 +301,14 @@
         verifyHighRes();
     }
 
+    /**
+     * Apply label and tag using a {@link RemoteActionItemInfo}
+     */
+    public void applyFromRemoteActionInfo(RemoteActionItemInfo remoteActionItemInfo) {
+        applyIconAndLabel(remoteActionItemInfo);
+        setTag(remoteActionItemInfo);
+    }
+
     private void applyIconAndLabel(ItemInfoWithIcon info) {
         FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
         mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
@@ -636,11 +653,14 @@
         mDisableRelayout = mIcon != null;
 
         icon.setBounds(0, 0, mIconSize, mIconSize);
-        if (mLayoutHorizontal) {
-            setCompoundDrawablesRelative(icon, null, null, null);
-        } else {
-            setCompoundDrawables(null, icon, null, null);
+
+        updateIcon(icon);
+
+        // If the current icon is a placeholder color, animate its update.
+        if (mIcon != null && mIcon instanceof PlaceHolderIconDrawable) {
+            animateIconUpdate((PlaceHolderIconDrawable) mIcon, icon);
         }
+
         mDisableRelayout = false;
     }
 
@@ -670,6 +690,8 @@
                 mActivity.invalidateParent(info);
             } else if (info instanceof PackageItemInfo) {
                 applyFromItemInfoWithIcon((PackageItemInfo) info);
+            } else if (info instanceof RemoteActionItemInfo) {
+                applyFromRemoteActionInfo((RemoteActionItemInfo) info);
             }
 
             mDisableRelayout = false;
@@ -776,4 +798,33 @@
             ((FastBitmapDrawable) mIcon).setScale(1f);
         }
     }
+
+    private void updateIcon(Drawable newIcon) {
+        if (mLayoutHorizontal) {
+            setCompoundDrawablesRelative(newIcon, null, null, null);
+        } else {
+            setCompoundDrawables(null, newIcon, null, null);
+        }
+    }
+
+    private static void animateIconUpdate(PlaceHolderIconDrawable oldIcon, Drawable newIcon) {
+        int placeholderColor = oldIcon.mPaint.getColor();
+        int originalAlpha = Color.alpha(placeholderColor);
+
+        ValueAnimator iconUpdateAnimation = ValueAnimator.ofInt(originalAlpha, 0);
+        iconUpdateAnimation.setDuration(ICON_UPDATE_ANIMATION_DURATION);
+        iconUpdateAnimation.addUpdateListener(valueAnimator -> {
+            int newAlpha = (int) valueAnimator.getAnimatedValue();
+            int newColor = ColorUtils.setAlphaComponent(placeholderColor, newAlpha);
+
+            newIcon.setColorFilter(new PorterDuffColorFilter(newColor, Mode.SRC_ATOP));
+        });
+        iconUpdateAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                newIcon.setColorFilter(null);
+            }
+        });
+        iconUpdateAnimation.start();
+    }
 }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 09827d6..df005e6 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -47,7 +47,6 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
@@ -395,6 +394,4 @@
                 TextUtils.TruncateAt.END);
         return !mText.equals(displayedText);
     }
-
-    public abstract Target getDropTargetForLogging();
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1cd201f..2809bd5 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -323,11 +323,8 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mTouchHelper != null
-                || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
-            return true;
-        }
-        return false;
+        return mTouchHelper != null
+                || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev));
     }
 
     public void enableHardwareLayer(boolean hasLayer) {
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 2857497..cc119c9 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -18,8 +18,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -28,22 +27,19 @@
 
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.Snackbar;
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
     private final StatsLogManager mStatsLogManager;
 
-    private int mControlType = ControlType.DEFAULT_CONTROLTYPE;
+    private StatsLogManager.LauncherEvent mLauncherEvent;
 
     public DeleteDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -115,8 +111,8 @@
      * Set mControlType depending on the drag item.
      */
     private void setControlTypeBasedOnDragSource(ItemInfo item) {
-        mControlType = item.id != ItemInfo.NO_ID ? ControlType.REMOVE_TARGET
-                : ControlType.CANCEL_TARGET;
+        mLauncherEvent = item.id != ItemInfo.NO_ID ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+                : LAUNCHER_ITEM_DROPPED_ON_CANCEL;
     }
 
     @Override
@@ -127,8 +123,7 @@
         }
         super.onDrop(d, options);
         mStatsLogManager.logger().withInstanceId(d.logInstanceId)
-                .log(mControlType == ControlType.REMOVE_TARGET ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
-                        : LAUNCHER_ITEM_DROPPED_ON_CANCEL);
+                .log(mLauncherEvent);
     }
 
     @Override
@@ -141,7 +136,7 @@
             Runnable onUndoClicked = () -> {
                 mLauncher.setPageToBindSynchronously(itemPage);
                 modelWriter.abortDelete();
-                mLauncher.getUserEventDispatcher().logActionOnControl(TAP, UNDO);
+                mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO);
             };
             Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
                     modelWriter::commitDelete, onUndoClicked);
@@ -161,11 +156,4 @@
         mLauncher.getDragLayer()
                 .announceForAccessibility(getContext().getString(R.string.item_removed));
     }
-
-    @Override
-    public Target getDropTargetForLogging() {
-        Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
-        t.controlType = mControlType;
-        return t;
-    }
 }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index d3b86de..139d4a8 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -33,6 +33,8 @@
 import android.graphics.drawable.Drawable;
 import android.util.Property;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.graphics.PlaceHolderIconDrawable;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -54,6 +56,8 @@
     protected Bitmap mBitmap;
     protected final int mIconColor;
 
+    @Nullable private ColorFilter mColorFilter;
+
     private boolean mIsPressed;
     private boolean mIsDisabled;
     private float mDisabledAlpha = 1f;
@@ -115,7 +119,8 @@
 
     @Override
     public void setColorFilter(ColorFilter cf) {
-        // No op
+        mColorFilter = cf;
+        updateFilter();
     }
 
     @Override
@@ -265,7 +270,7 @@
      * Updates the paint to reflect the current brightness and saturation.
      */
     protected void updateFilter() {
-        mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null);
+        mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter);
         invalidateSelf();
     }
 
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 51f3819..6547b53 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -27,14 +25,10 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-
-import java.util.ArrayList;
-
-public class Hotseat extends CellLayout implements LogContainerProvider, Insettable {
+/**
+ * View class that represents the bottom row of the home screen.
+ */
+public class Hotseat extends CellLayout implements Insettable {
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mHasVerticalHotseat;
@@ -79,15 +73,6 @@
     }
 
     @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        child.rank = childInfo.rank;
-        child.gridX = childInfo.cellX;
-        child.gridY = childInfo.cellY;
-        parents.add(newContainerTarget(LauncherLogProto.ContainerType.HOTSEAT));
-    }
-
-    @Override
     public void setInsets(Rect insets) {
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
         DeviceProfile grid = mActivity.getDeviceProfile();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4c5224b..ab8d7a5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
@@ -36,11 +37,10 @@
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
-import static com.android.launcher3.logging.StatsLogManager.containerTypeToAtomState;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
@@ -70,6 +70,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -89,9 +90,10 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
 import android.widget.Toast;
 
 import androidx.annotation.CallSuper;
@@ -118,13 +120,13 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.keyboard.CustomActionsPopup;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.ModelUtils;
@@ -152,9 +154,6 @@
 import com.android.launcher3.touch.AllAppsSwipeController;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.ComponentKey;
@@ -270,6 +269,8 @@
     private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
     private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
 
+    private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
+
     private LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
@@ -401,6 +402,7 @@
 
         inflateRootView(R.layout.launcher);
         setupViews();
+        crossFadeWithPreviousAppearance();
         mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 
         mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
@@ -477,7 +479,7 @@
                 () -> getStateManager().goToState(NORMAL));
 
         if (Utilities.ATLEAST_R) {
-            getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+            getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
         }
 
         mLifecycleRegistry = new LifecycleRegistry(this);
@@ -555,7 +557,6 @@
     }
 
     private void onIdpChanged(InvariantDeviceProfile idp) {
-        mUserEventDispatcher = null;
 
         initDeviceProfile(idp);
         dispatchDeviceProfileChanged();
@@ -911,7 +912,7 @@
             mOverlayManager.onActivityStopped(this);
         }
 
-        logStopAndResume(Action.Command.STOP);
+        logStopAndResume(false /* isResume */);
         mAppWidgetHost.setListenIfResumed(false);
         NotificationListener.removeNotificationsChangedListener();
     }
@@ -933,7 +934,7 @@
     @Override
     @CallSuper
     protected void onDeferredResumed() {
-        logStopAndResume(Action.Command.RESUME);
+        logStopAndResume(true /* isResume */);
 
         // Process any items that were added while Launcher was away.
         ItemInstallQueue.INSTANCE.get(this)
@@ -950,32 +951,28 @@
 
     protected void handlePendingActivityRequest() { }
 
-    private void logStopAndResume(int command) {
+    private void logStopAndResume(boolean isResume) {
         if (mPendingExecutor != null) return;
         int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
-        int containerType = mStateManager.getState().containerType;
+        int statsLogOrdinal = mStateManager.getState().statsLogOrdinal;
 
         StatsLogManager.EventEnum event;
         StatsLogManager.StatsLogger logger = getStatsLogManager().logger();
-        if (command == Action.Command.RESUME) {
+        if (isResume) {
             logger.withSrcState(LAUNCHER_STATE_BACKGROUND)
-                .withDstState(containerTypeToAtomState(mStateManager.getState().containerType));
+                .withDstState(mStateManager.getState().statsLogOrdinal);
             event = LAUNCHER_ONRESUME;
         } else { /* command == Action.Command.STOP */
-            logger.withSrcState(containerTypeToAtomState(mStateManager.getState().containerType))
+            logger.withSrcState(mStateManager.getState().statsLogOrdinal)
                     .withDstState(LAUNCHER_STATE_BACKGROUND);
             event = LAUNCHER_ONSTOP;
         }
 
-        if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
-            getUserEventDispatcher().logActionCommand(command,
-                    containerType, -1, pageIndex);
+        if (statsLogOrdinal == LAUNCHER_STATE_HOME && mWorkspace != null) {
             logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
                     .setWorkspace(
                             LauncherAtom.WorkspaceContainer.newBuilder()
                                     .setPageIndex(pageIndex)).build());
-        } else {
-            getUserEventDispatcher().logActionCommand(command, containerType, -1);
         }
         logger.log(event);
     }
@@ -1365,6 +1362,18 @@
         closeContextMenu();
     }
 
+    @Override
+    public Object onRetainNonConfigurationInstance() {
+        int width = mDragLayer.getWidth();
+        int height = mDragLayer.getHeight();
+
+        if (width <= 0 || height <= 0) {
+            return null;
+        }
+
+        return BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
+    }
+
     public AllAppsTransitionController getAllAppsController() {
         return mAllAppsController;
     }
@@ -1465,11 +1474,6 @@
             }
 
             // Handle HOME_INTENT
-            UserEventDispatcher ued = getUserEventDispatcher();
-            Target target = newContainerTarget(mStateManager.getState().containerType);
-            target.pageIndex = mWorkspace.getCurrentPage();
-            ued.logActionCommand(Action.Command.HOME_INTENT, target,
-                    newContainerTarget(ContainerType.WORKSPACE));
             hideKeyboard();
 
             if (mLauncherCallbacks != null) {
@@ -2759,4 +2763,40 @@
 
         void onLauncherResume();
     }
+
+    /**
+     * Cross-fades the launcher's updated appearance with its previous appearance.
+     *
+     * This method is used to cross-fade UI updates on activity creation, specifically dark mode
+     * updates.
+     */
+    private void crossFadeWithPreviousAppearance() {
+        Bitmap previousAppearanceBitmap = (Bitmap) getLastNonConfigurationInstance();
+
+        if (previousAppearanceBitmap == null) {
+            return;
+        }
+
+        ImageView crossFadeHelper = new ImageView(this);
+
+        crossFadeHelper.setImageBitmap(previousAppearanceBitmap);
+        crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
+        InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
+                InsettableFrameLayout.LayoutParams.MATCH_PARENT,
+                InsettableFrameLayout.LayoutParams.MATCH_PARENT);
+
+        layoutParams.ignoreInsets = true;
+
+        crossFadeHelper.setLayoutParams(layoutParams);
+
+        getRootView().addView(crossFadeHelper);
+
+        crossFadeHelper
+                .animate()
+                .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION)
+                .alpha(0f)
+                .withEndAction(() -> getRootView().removeView(crossFadeHelper))
+                .start();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index bfe327e..a4181c5 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -105,7 +105,7 @@
         new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
 
         mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
-                .registerInstallTracker(mModel, MODEL_EXECUTOR);
+                .registerInstallTracker(mModel);
 
         // Register an observer to rebind the notification listener when dots are re-enabled.
         mNotificationDotsObserver =
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index b6bc500..eba0ac9 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -16,6 +16,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
@@ -35,7 +36,6 @@
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.uioverrides.states.AllAppsState;
 import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import java.util.Arrays;
 
@@ -97,7 +97,7 @@
      * TODO: Create a separate class for NORMAL state.
      */
     public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
-            ContainerType.WORKSPACE,
+            LAUNCHER_STATE_HOME,
             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
                     FLAG_HAS_SYS_UI_SCRIM) {
         @Override
@@ -126,9 +126,9 @@
     public final int ordinal;
 
     /**
-     * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
+     * Used for {@link com.android.launcher3.logging.StatsLogManager}
      */
-    public final int containerType;
+    public final int statsLogOrdinal;
 
     /**
      * True if the state has overview panel visible.
@@ -137,8 +137,8 @@
 
     private final int mFlags;
 
-    public LauncherState(int id, int containerType, int flags) {
-        this.containerType = containerType;
+    public LauncherState(int id, int statsLogOrdinal, int flags) {
+        this.statsLogOrdinal = statsLogOrdinal;
         this.mFlags = flags;
         this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
         this.ordinal = id;
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 2df7f5a..92b88e6 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -37,14 +37,11 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Themes;
 
@@ -135,19 +132,6 @@
     }
 
     @Override
-    public Target getDropTargetForLogging() {
-        Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
-        if (mCurrentAccessibilityAction == UNINSTALL) {
-            t.controlType = ControlType.UNINSTALL_TARGET;
-        } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
-            t.controlType = ControlType.DISMISS_PREDICTION;
-        } else {
-            t.controlType = ControlType.SETTINGS_BUTTON;
-        }
-        return t;
-    }
-
-    @Override
     protected boolean supportsDrop(ItemInfo info) {
         return supportsAccessibilityDrop(info, getViewUnderDrag(info));
     }
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 007e5f5..1bbbb2b 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -25,8 +25,11 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.util.Executors;
 
 /**
  * BroadcastReceiver to handle session commit intent.
@@ -38,6 +41,11 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
+        Executors.MODEL_EXECUTOR.execute(() -> processIntent(context, intent));
+    }
+
+    @WorkerThread
+    private static void processIntent(Context context, Intent intent) {
         if (!isEnabled(context)) {
             // User has decided to not add icons on homescreen.
             return;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index dea2a8d..43ccb79 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -133,6 +133,10 @@
     public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
     public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
 
+    // An intent extra to indicate the launch source by launcher.
+    public static final String EXTRA_WALLPAPER_LAUNCH_SOURCE =
+            "com.android.wallpaper.LAUNCH_SOURCE";
+
     public static boolean IS_RUNNING_IN_TEST_HARNESS =
                     ActivityManager.isRunningInTestHarness();
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 45aaa1b..98328cf 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -85,7 +85,6 @@
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -97,8 +96,6 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.WorkspaceTouchListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
@@ -1004,8 +1001,6 @@
     public void onOverlayScrollChanged(float scroll) {
         if (Float.compare(scroll, 1f) == 0) {
             if (!mOverlayShown) {
-                mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
-                        Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
                 mLauncher.getStatsLogManager().logger()
                         .withSrcState(LAUNCHER_STATE_HOME)
                         .withDstState(LAUNCHER_STATE_HOME)
@@ -1020,20 +1015,16 @@
             // Not announcing the overlay page for accessibility since it announces itself.
         } else if (Float.compare(scroll, 0f) == 0) {
             if (mOverlayShown) {
-                UserEventDispatcher ued = mLauncher.getUserEventDispatcher();
-                if (!ued.isPreviousHomeGesture()) {
-                    mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
-                        Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
-                    mLauncher.getStatsLogManager().logger()
-                            .withSrcState(LAUNCHER_STATE_HOME)
-                            .withDstState(LAUNCHER_STATE_HOME)
-                            .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
-                                    .setWorkspace(
-                                            LauncherAtom.WorkspaceContainer.newBuilder()
-                                                    .setPageIndex(-1))
-                                    .build())
-                            .log(LAUNCHER_SWIPERIGHT);
-                }
+                // TODO: this is logged unnecessarily on home gesture.
+                mLauncher.getStatsLogManager().logger()
+                        .withSrcState(LAUNCHER_STATE_HOME)
+                        .withDstState(LAUNCHER_STATE_HOME)
+                        .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                                .setWorkspace(
+                                        LauncherAtom.WorkspaceContainer.newBuilder()
+                                                .setPageIndex(-1))
+                                .build())
+                        .log(LAUNCHER_SWIPERIGHT);
             } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
                 // When arriving to 0 overscroll from non-zero overscroll, announce page for
                 // accessibility since default announcements were disabled while in overscroll
@@ -1124,12 +1115,8 @@
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
         if (prevPage != mCurrentPage) {
-            int swipeDirection = (prevPage < mCurrentPage)
-                    ? Action.Direction.RIGHT : Action.Direction.LEFT;
             StatsLogManager.EventEnum event = (prevPage < mCurrentPage)
                     ? LAUNCHER_SWIPERIGHT : LAUNCHER_SWIPELEFT;
-            mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
-                    swipeDirection, ContainerType.WORKSPACE, prevPage);
             mLauncher.getStatsLogManager().logger()
                     .withSrcState(LAUNCHER_STATE_HOME)
                     .withDstState(LAUNCHER_STATE_HOME)
@@ -1176,13 +1163,6 @@
     }
 
     @Override
-    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
-        if (!isSwitchingState()) {
-            super.determineScrollingStart(ev, touchSlopScale);
-        }
-    }
-
-    @Override
     public void announceForAccessibility(CharSequence text) {
         // Don't announce if apps is on top of us.
         if (!mLauncher.isInState(ALL_APPS)) {
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index ea887cc..d6302ce 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -130,12 +130,16 @@
         }
 
         child.setHapticFeedbackEnabled(false);
-        child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+        child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
         if (child instanceof DropTarget) {
             onAddDropTarget((DropTarget) child);
         }
     }
 
+    default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
+        return ItemLongClickListener.INSTANCE_WORKSPACE;
+    }
+
     Hotseat getHotseat();
 
     CellLayout getScreenWithId(int screenId);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index d1340fa..8bc8e53 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -92,10 +92,14 @@
 
     public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 9;
 
-    public static final int VIEW_TYPE_SEARCH_SHORTCUT = 1 << 10;
+    public static final int VIEW_TYPE_SEARCH_ICON_ROW = 1 << 10;
 
     public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11;
 
+    public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12;
+
+    public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
+
     // 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;
@@ -186,7 +190,9 @@
                     || viewType == VIEW_TYPE_SEARCH_SLICE
                     || viewType == VIEW_TYPE_SEARCH_ROW
                     || viewType == VIEW_TYPE_SEARCH_PEOPLE
-                    || viewType == VIEW_TYPE_SEARCH_SHORTCUT;
+                    || viewType == VIEW_TYPE_SEARCH_THUMBNAIL
+                    || viewType == VIEW_TYPE_SEARCH_ICON_ROW
+                    || viewType == VIEW_TYPE_SEARCH_SUGGEST;
         }
     }
 
@@ -197,6 +203,7 @@
      */
     public static class AdapterItemWithPayload<T> extends AdapterItem {
         private T mPayload;
+        private String mSearchSessionId;
         private AllAppsSearchPlugin mPlugin;
         private IntConsumer mSelectionHandler;
 
@@ -218,6 +225,14 @@
             mSelectionHandler = runnable;
         }
 
+        public void setSearchSessionId(String searchSessionId) {
+            mSearchSessionId = searchSessionId;
+        }
+
+        public String getSearchSessionId() {
+            return mSearchSessionId;
+        }
+
         public IntConsumer getSelectionHandler() {
             return mSelectionHandler;
         }
@@ -225,6 +240,8 @@
         public T getPayload() {
             return mPayload;
         }
+
+
     }
 
     /**
@@ -307,15 +324,21 @@
 
         @Override
         public int getSpanSize(int position) {
-            if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
-                return 1;
+            int viewType = mApps.getAdapterItems().get(position).viewType;
+            if (isIconViewType(viewType)) {
+                return 1 * SPAN_MULTIPLIER;
+            } else if (viewType == VIEW_TYPE_SEARCH_THUMBNAIL) {
+                return mAppsPerRow;
             } else {
                 // Section breaks span the full width
-                return mAppsPerRow;
+                return mAppsPerRow * SPAN_MULTIPLIER;
             }
         }
     }
 
+    // multiplier to support adapter item column count that is not mAppsPerRow.
+    public static final int SPAN_MULTIPLIER = 3;
+
     private final BaseDraggingActivity mLauncher;
     private final LayoutInflater mLayoutInflater;
     private final AlphabeticalAppsList mApps;
@@ -352,7 +375,7 @@
 
     public void setAppsPerRow(int appsPerRow) {
         mAppsPerRow = appsPerRow;
-        mGridLayoutMgr.setSpanCount(mAppsPerRow);
+        mGridLayoutMgr.setSpanCount(mAppsPerRow * SPAN_MULTIPLIER);
     }
 
     /**
@@ -438,12 +461,18 @@
             case VIEW_TYPE_SEARCH_SLICE:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.search_result_slice, parent, false));
-            case VIEW_TYPE_SEARCH_SHORTCUT:
+            case VIEW_TYPE_SEARCH_ICON_ROW:
                 return new ViewHolder(mLayoutInflater.inflate(
-                        R.layout.search_result_shortcut, parent, false));
+                        R.layout.search_result_icon_row, parent, false));
             case VIEW_TYPE_SEARCH_PEOPLE:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.search_result_people_item, parent, false));
+            case VIEW_TYPE_SEARCH_THUMBNAIL:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.search_result_thumbnail, parent, false));
+            case VIEW_TYPE_SEARCH_SUGGEST:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.search_result_suggest, parent, false));
             default:
                 throw new RuntimeException("Unexpected view type");
         }
@@ -464,24 +493,27 @@
                 //TODO: replace with custom TopHitBubbleTextView with support for both shortcut
                 // and apps
                 if (adapterItem instanceof AdapterItemWithPayload) {
-                    AdapterItemWithPayload withPayload = (AdapterItemWithPayload) adapterItem;
-                    IntConsumer selectionHandler = type -> {
+                    AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
+                    item.setSelectionHandler(type -> {
                         SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
-                                type);
+                                type, item.position, item.getSearchSessionId());
                         e.bundle = HeroSearchResultView.getAppBundle(info);
-                        if (withPayload.getPlugin() != null) {
-                            withPayload.getPlugin().notifySearchTargetEvent(e);
+                        if (item.getPlugin() != null) {
+                            item.getPlugin().notifySearchTargetEvent(e);
                         }
-                    };
+                    });
                     icon.setOnClickListener(view -> {
-                        selectionHandler.accept(SearchTargetEvent.SELECT);
+                        item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
                         mOnIconClickListener.onClick(view);
                     });
                     icon.setOnLongClickListener(view -> {
-                        selectionHandler.accept(SearchTargetEvent.LONG_PRESS);
+                        item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
                         return mOnIconLongClickListener.onLongClick(view);
                     });
-                    withPayload.setSelectionHandler(selectionHandler);
+                }
+                else {
+                    icon.setOnClickListener(mOnIconClickListener);
+                    icon.setOnLongClickListener(mOnIconLongClickListener);
                 }
                 break;
             case VIEW_TYPE_EMPTY_SEARCH:
@@ -500,20 +532,22 @@
                 break;
             case VIEW_TYPE_SEARCH_SLICE:
                 SliceView sliceView = (SliceView) holder.itemView;
-                AdapterItemWithPayload<Uri> item =
+                AdapterItemWithPayload<Uri> slicePayload =
                         (AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
                 sliceView.setOnSliceActionListener((info1, s) -> {
-                    if (item.getPlugin() != null) {
+                    if (slicePayload.getPlugin() != null) {
                         SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
                                 SearchTarget.ItemType.SETTINGS_SLICE,
-                                SearchTargetEvent.CHILD_SELECT);
+                                SearchTargetEvent.CHILD_SELECT, slicePayload.position,
+                                slicePayload.getSearchSessionId());
                         searchTargetEvent.bundle = new Bundle();
-                        searchTargetEvent.bundle.putParcelable("uri", item.getPayload());
-                        item.getPlugin().notifySearchTargetEvent(searchTargetEvent);
+                        searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
+                        slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
                     }
                 });
                 try {
-                    LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher, item.getPayload());
+                    LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
+                            slicePayload.getPayload());
                     liveData.observe((Launcher) mLauncher, sliceView);
                     sliceView.setTag(liveData);
                 } catch (Exception ignored) {
@@ -523,11 +557,14 @@
             case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
             case VIEW_TYPE_SEARCH_HERO_APP:
             case VIEW_TYPE_SEARCH_ROW:
-            case VIEW_TYPE_SEARCH_SHORTCUT:
+            case VIEW_TYPE_SEARCH_ICON_ROW:
             case VIEW_TYPE_SEARCH_PEOPLE:
+            case VIEW_TYPE_SEARCH_THUMBNAIL:
+            case VIEW_TYPE_SEARCH_SUGGEST:
+                AdapterItemWithPayload item =
+                        (AdapterItemWithPayload) mApps.getAdapterItems().get(position);
                 PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
-                payloadResultView.applyAdapterInfo(
-                        (AdapterItemWithPayload) mApps.getAdapterItems().get(position));
+                payloadResultView.setup(item);
                 break;
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 // nothing to do
@@ -541,8 +578,8 @@
         if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
         if (holder.itemView instanceof BubbleTextView) {
             BubbleTextView icon = (BubbleTextView) holder.itemView;
-            icon.setOnClickListener(mOnIconClickListener);
-            icon.setOnLongClickListener(mOnIconLongClickListener);
+            icon.setOnClickListener(null);
+            icon.setOnLongClickListener(null);
         } else if (holder.itemView instanceof SliceView) {
             SliceView sliceView = (SliceView) holder.itemView;
             sliceView.setOnSliceActionListener(null);
@@ -553,7 +590,6 @@
         }
     }
 
-
     @Override
     public boolean onFailedToRecycleView(ViewHolder holder) {
         // Always recycle and we will reset the view when it is bound
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 13a93ff..72b6d94 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,8 +19,6 @@
 import static android.view.View.MeasureSpec.UNSPECIFIED;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -38,10 +36,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.ArrayList;
@@ -50,7 +44,7 @@
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
-public class AllAppsRecyclerView extends BaseRecyclerView implements LogContainerProvider {
+public class AllAppsRecyclerView extends BaseRecyclerView {
 
     private AlphabeticalAppsList mApps;
     private final int mNumAppsPerRow;
@@ -176,13 +170,6 @@
         mAutoSizedOverlays.clear();
     }
 
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        parents.add(newContainerTarget(
-                getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
-    }
-
     public void onSearchResultsChanged() {
         // Always scroll the view to the top so the user can see the changed results
         scrollToTop();
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 14595ca..0005db8 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -18,8 +18,6 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.HOTSEAT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.PREDICTION;
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
@@ -120,10 +118,10 @@
         return (type & TYPE_DISCOVERY_BOUNCE) != 0;
     }
 
-    private void show(int containerType) {
+    private void show() {
         mIsOpen = true;
         mLauncher.getDragLayer().addView(this);
-        mLauncher.getUserEventDispatcher().logActionBounceTip(containerType);
+        // TODO: add WW log for discovery bounce tip show event.
     }
 
     public static void showForHomeIfNeeded(Launcher launcher) {
@@ -146,7 +144,7 @@
         }
         onboardingPrefs.incrementEventCount(OnboardingPrefs.HOME_BOUNCE_COUNT);
 
-        new DiscoveryBounce(launcher, 0).show(HOTSEAT);
+        new DiscoveryBounce(launcher, 0).show();
     }
 
     public static void showForOverviewIfNeeded(Launcher launcher,
@@ -179,7 +177,7 @@
         onboardingPrefs.incrementEventCount(OnboardingPrefs.SHELF_BOUNCE_COUNT);
 
         new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
-                .show(PREDICTION);
+                .show();
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 3320189..d7fa5bc 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -35,6 +35,8 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -213,6 +215,44 @@
         /**
          * Updates View using Adapter's payload
          */
+
+        default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
+            Object[] targetInfo = getTargetInfo();
+            if (targetInfo != null) {
+                targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
+                targetInfo[1] = adapterItemWithPayload.position;
+            }
+            applyAdapterInfo(adapterItemWithPayload);
+        }
+
         void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
+
+        /**
+         * Gets object created by {@link PayloadResultHandler#createTargetInfo()}
+         */
+        Object[] getTargetInfo();
+
+        /**
+         * Creates a wrapper object to hold searchSessionId and item position
+         */
+        default Object[] createTargetInfo() {
+            return new Object[2];
+        }
+
+        /**
+         * Generates a SearchTargetEvent object for a PayloadHandlerView
+         */
+        default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
+                int eventType) {
+            Object[] targetInfo = getTargetInfo();
+            if (targetInfo == null) return null;
+
+            String searchSessionId = (String) targetInfo[0];
+            int position = (int) targetInfo[1];
+            return new SearchTargetEvent(itemType, eventType,
+                    position, searchSessionId);
+        }
     }
+
+
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 5362575..6dd316e 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -73,9 +73,9 @@
         addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders);
     }
 
-    public void finish(boolean isSuccess, int logAction) {
+    public void finish(boolean isSuccess) {
         for (Consumer<EndState> listeners : mEndListeners) {
-            listeners.accept(new EndState(isSuccess, logAction));
+            listeners.accept(new EndState(isSuccess));
         }
         mEndListeners.clear();
     }
@@ -164,7 +164,7 @@
 
     /**
      * Add a listener of receiving the end state.
-     * Note that the listeners are called as a result of calling {@link #finish(boolean, int)}
+     * Note that the listeners are called as a result of calling {@link #finish(boolean)}
      * and not automatically
      */
     public void addEndListener(Consumer<EndState> listener) {
@@ -173,11 +173,9 @@
 
     public static class EndState {
         public boolean isSuccess;
-        public int logAction;
 
-        public EndState(boolean isSuccess, int logAction) {
+        public EndState(boolean isSuccess) {
             this.isSuccess = isSuccess;
-            this.logAction = logAction;
         }
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 2d625c5..42e247a 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,10 +16,11 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_BACK;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_START;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.annotation.TargetApi;
@@ -49,11 +50,10 @@
 import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.pm.PinRequestHelper;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -125,7 +125,7 @@
         // savedInstanceState is null when the activity is created the first time (i.e., avoids
         // duplicate logging during rotation)
         if (savedInstanceState == null) {
-            logCommand(Action.Command.ENTRY);
+            logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
         }
     }
 
@@ -178,6 +178,7 @@
         startActivity(homeIntent,
                 ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out)
                         .toBundle());
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
         mFinishOnPause = true;
         return false;
     }
@@ -240,7 +241,7 @@
      * Called when the cancel button is clicked.
      */
     public void onCancelClick(View v) {
-        logCommand(Action.Command.CANCEL);
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED);
         finish();
     }
 
@@ -250,7 +251,7 @@
     public void onPlaceAutomaticallyClick(View v) {
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
             ItemInstallQueue.INSTANCE.get(this).queueItem(mRequest.getShortcutInfo());
-            logCommand(Action.Command.CONFIRM);
+            logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
             mRequest.accept();
             finish();
             return;
@@ -274,13 +275,13 @@
                 .queueItem(mRequest.getAppWidgetProviderInfo(this), widgetId);
         mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
         mRequest.accept(mWidgetOptions);
-        logCommand(Action.Command.CONFIRM);
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
         finish();
     }
 
     @Override
     public void onBackPressed() {
-        logCommand(Action.Command.BACK);
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_BACK);
         super.onBackPressed();
     }
 
@@ -320,10 +321,7 @@
         throw new UnsupportedOperationException();
     }
 
-    private void logCommand(int command) {
-        getUserEventDispatcher().dispatchUserEvent(newLauncherEvent(
-                newCommandAction(command),
-                newItemTarget(mWidgetCell.getWidgetView(), mInstantAppResolver),
-                newContainerTarget(ContainerType.PINITEM)), null);
+    private void logCommand(StatsLogManager.EventEnum command) {
+        getStatsLogManager().logger().log(command);
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index ef666f0..1cfe6ac 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -206,7 +206,6 @@
         }
 
         handleMoveEvent(mLastTouch.x, mLastTouch.y);
-        mLauncher.getUserEventDispatcher().resetActionDurationMillis();
 
         if (!mLauncher.isTouchInProgress() && options.simulatedDndStartPoint == null) {
             // If it is an internal drag and the touch is already complete, cancel immediately
@@ -544,7 +543,6 @@
             }
         }
         final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
-        mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
         dispatchDropComplete(dropTargetAsView, accepted);
     }
 
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 281598a..63fa391 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -23,7 +23,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
@@ -95,7 +94,6 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ClipPathView;
@@ -599,15 +597,6 @@
      * is played.
      */
     private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
-        animateOpen(items, pageNo, false);
-    }
-
-    /**
-     * Opens the user folder described by the specified tag. The opening of the folder
-     * is animated relative to the specified View. If the View is null, no animation
-     * is played.
-     */
-    private void animateOpen(List<WorkspaceItemInfo> items, int pageNo, boolean skipUserEventLog) {
         Folder openFolder = getOpen(mLauncher);
         if (openFolder != null && openFolder != this) {
             // Close any open folder before opening a folder.
@@ -657,14 +646,6 @@
                 mState = STATE_OPEN;
                 announceAccessibilityChanges();
 
-                if (!skipUserEventLog) {
-                    mLauncher.getUserEventDispatcher().logActionOnItem(
-                            LauncherLogProto.Action.Touch.TAP,
-                            LauncherLogProto.Action.Direction.NONE,
-                            LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
-                }
-
-
                 mContent.setFocusOnFirstChild();
             }
         });
@@ -1513,7 +1494,6 @@
                 }
 
                 statsLogger.log(LAUNCHER_FOLDER_LABEL_UPDATED);
-                logFolderLabelState(mFromLabelState, toLabelState);
                 mFolderName.dispatchBackKey();
             }
         }
@@ -1644,8 +1624,7 @@
                         return true;
                     }
                 } else {
-                    mLauncher.getUserEventDispatcher().logActionTapOutside(
-                            newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
+                    // TODO: add ww log if need to gather tap outside to close folder
                     close(true);
                     return true;
                 }
@@ -1680,17 +1659,6 @@
         return mContent;
     }
 
-    /**
-     * Logs current folder label info.
-     *
-     * @deprecated This method is only used for log validation and soon will be removed.
-     */
-    @Deprecated
-    public void logFolderLabelState(FromState fromState, ToState toState) {
-        mLauncher.getUserEventDispatcher()
-                .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
-    }
-
     /** Returns the height of the current folder's bottom edge from the bottom of the screen. */
     private int getHeightFromBottom() {
         DragLayer.LayoutParams layoutParams = (DragLayer.LayoutParams) getLayoutParams();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 32d061c..3296eed 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -478,7 +478,6 @@
                 // event is assumed to be folder creation on the server side.
                 .withEditText(newTitle.toString())
                 .log(LAUNCHER_FOLDER_AUTO_LABELED);
-        mFolder.logFolderLabelState(fromState, ToState.TO_SUGGESTION0);
     }
 
 
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index cd84c96..effb3a4 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -513,8 +513,7 @@
         }
 
         // Setup search view
-        SearchUiManager searchUiManager =
-                mRootView.findViewById(R.id.search_container_all_apps);
+        SearchUiManager searchUiManager = mRootView.findViewById(R.id.search_container_all_apps);
         mRootView.findViewById(R.id.apps_view).setTranslationY(
                 mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets));
         ViewGroup searchView = (ViewGroup) searchUiManager;
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
deleted file mode 100644
index cd4f034..0000000
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2016 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.logging;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-import android.view.View;
-
-import com.android.launcher3.ButtonDropTarget;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.InstantAppResolver;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-
-/**
- * Helper methods for logging.
- */
-public class LoggerUtils {
-    private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
-    private static final String UNKNOWN = "UNKNOWN";
-    private static final int DEFAULT_PREDICTED_RANK = 10000;
-    private static final String DELIMITER_DOT = "\\.";
-
-    public static String getFieldName(int value, Class c) {
-        SparseArray<String> cache;
-        synchronized (sNameCache) {
-            cache = sNameCache.get(c);
-            if (cache == null) {
-                cache = new SparseArray<>();
-                for (Field f : c.getDeclaredFields()) {
-                    if (f.getType() == int.class && Modifier.isStatic(f.getModifiers())) {
-                        try {
-                            f.setAccessible(true);
-                            cache.put(f.getInt(null), f.getName());
-                        } catch (IllegalAccessException e) {
-                            // Ignore
-                        }
-                    }
-                }
-                sNameCache.put(c, cache);
-            }
-        }
-        String result = cache.get(value);
-        return result != null ? result : UNKNOWN;
-    }
-
-    public static Target newItemTarget(int itemType) {
-        Target t = newTarget(Target.Type.ITEM);
-        t.itemType = itemType;
-        return t;
-    }
-
-    public static Target newItemTarget(View v, InstantAppResolver instantAppResolver) {
-        return (v != null) && (v.getTag() instanceof ItemInfo)
-                ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver)
-                : newTarget(Target.Type.ITEM);
-    }
-
-    public static Target newItemTarget(ItemInfo info, InstantAppResolver instantAppResolver) {
-        Target t = newTarget(Target.Type.ITEM);
-        switch (info.itemType) {
-            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                t.itemType = (instantAppResolver != null && info instanceof AppInfo
-                        && instantAppResolver.isInstantApp(((AppInfo) info)))
-                        ? ItemType.WEB_APP
-                        : ItemType.APP_ICON;
-                t.predictedRank = DEFAULT_PREDICTED_RANK;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                t.itemType = ItemType.SHORTCUT;
-                t.predictedRank = DEFAULT_PREDICTED_RANK;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                t.itemType = ItemType.FOLDER_ICON;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                t.itemType = ItemType.WIDGET;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                t.itemType = ItemType.DEEPSHORTCUT;
-                t.predictedRank = DEFAULT_PREDICTED_RANK;
-                break;
-        }
-        return t;
-    }
-
-    public static Target newDropTarget(View v) {
-        if (!(v instanceof ButtonDropTarget)) {
-            return newTarget(Target.Type.CONTAINER);
-        }
-        if (v instanceof ButtonDropTarget) {
-            return ((ButtonDropTarget) v).getDropTargetForLogging();
-        }
-        return newTarget(Target.Type.CONTROL);
-    }
-
-    public static Target newTarget(int targetType, TargetExtension extension) {
-        Target t = new Target();
-        t.type = targetType;
-        t.extension = extension;
-        return t;
-    }
-
-    public static Target newTarget(int targetType) {
-        Target t = new Target();
-        t.type = targetType;
-        return t;
-    }
-
-    public static Target newControlTarget(int controlType) {
-        Target t = newTarget(Target.Type.CONTROL);
-        t.controlType = controlType;
-        return t;
-    }
-
-    public static Target newContainerTarget(int containerType) {
-        Target t = newTarget(Target.Type.CONTAINER);
-        t.containerType = containerType;
-        return t;
-    }
-
-    public static Action newAction(int type) {
-        Action a = new Action();
-        a.type = type;
-        return a;
-    }
-
-    public static Action newCommandAction(int command) {
-        Action a = newAction(Action.Type.COMMAND);
-        a.command = command;
-        return a;
-    }
-
-    public static Action newTouchAction(int touch) {
-        Action a = newAction(Action.Type.TOUCH);
-        a.touch = touch;
-        return a;
-    }
-
-    public static LauncherEvent newLauncherEvent(Action action, Target... srcTargets) {
-        LauncherEvent event = new LauncherEvent();
-        event.srcTarget = srcTargets;
-        event.action = action;
-        return event;
-    }
-
-    /**
-     * Creates LauncherEvent using Action and ArrayList of Targets
-     */
-    public static LauncherEvent newLauncherEvent(Action action, ArrayList<Target> targets) {
-        Target[] targetsArray = new Target[targets.size()];
-        targets.toArray(targetsArray);
-        return newLauncherEvent(action, targetsArray);
-    }
-
-    /**
-     * String conversion for only the helpful parts of {@link Object#toString()} method
-     * @param stringToExtract "foo.bar.baz.MyObject@1234"
-     * @return "MyObject@1234"
-     */
-    public static String extractObjectNameAndAddress(String stringToExtract) {
-        String[] superStringParts = stringToExtract.split(DELIMITER_DOT);
-        if (superStringParts.length == 0) {
-            return "";
-        }
-        return superStringParts[superStringParts.length - 1];
-    }
-}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index ec1c3ef..2c5bf32 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -27,7 +27,6 @@
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.LauncherLogProto;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 import java.util.List;
@@ -51,40 +50,22 @@
     public static final int LAUNCHER_STATE_UNCHANGED = 5;
 
     /**
-     * Returns proper launcher state enum for {@link StatsLogManager}(to be removed during
-     * UserEventDispatcher cleanup)
-     */
-    public static int containerTypeToAtomState(int containerType) {
-        switch (containerType) {
-            case LauncherLogProto.ContainerType.ALLAPPS_VALUE:
-                return LAUNCHER_STATE_ALLAPPS;
-            case LauncherLogProto.ContainerType.OVERVIEW_VALUE:
-                return LAUNCHER_STATE_OVERVIEW;
-            case LauncherLogProto.ContainerType.WORKSPACE_VALUE:
-                return LAUNCHER_STATE_HOME;
-            case LauncherLogProto.ContainerType.APP_VALUE:
-                return LAUNCHER_STATE_BACKGROUND;
-        }
-        return LAUNCHER_STATE_UNSPECIFIED;
-    }
-
-    /**
-     * Returns event enum based on the two {@link ContainerType} transition information when swipe
+     * Returns event enum based on the two state transition information when swipe
      * gesture happens(to be removed during UserEventDispatcher cleanup).
      */
-    public static EventEnum getLauncherAtomEvent(int startContainerType,
-            int targetContainerType, EventEnum fallbackEvent) {
-        if (startContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()
-                && targetContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()) {
+    public static EventEnum getLauncherAtomEvent(int startState,
+            int targetState, EventEnum fallbackEvent) {
+        if (startState == LAUNCHER_STATE_HOME
+                && targetState == LAUNCHER_STATE_HOME) {
             return LAUNCHER_HOME_GESTURE;
-        } else if (startContainerType != LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()
-                && targetContainerType == LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()) {
+        } else if (startState != LAUNCHER_STATE_OVERVIEW
+                && targetState == LAUNCHER_STATE_OVERVIEW) {
             return LAUNCHER_OVERVIEW_GESTURE;
-        } else if (startContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()
-                && targetContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+        } else if (startState != LAUNCHER_STATE_ALLAPPS
+                && targetState == LAUNCHER_STATE_ALLAPPS) {
             return LAUNCHER_ALLAPPS_OPEN_UP;
-        } else if (startContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()
-                && targetContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+        } else if (startState == LAUNCHER_STATE_ALLAPPS
+                && targetState != LAUNCHER_STATE_ALLAPPS) {
             return LAUNCHER_ALLAPPS_CLOSE_DOWN;
         }
         return fallbackEvent; // TODO fix
@@ -322,7 +303,38 @@
         LAUNCHER_FOLDER_CONVERTED_TO_ICON(628),
 
         @UiEvent(doc = "A hotseat prediction item was pinned")
-        LAUNCHER_HOTSEAT_PREDICTION_PINNED(629);
+        LAUNCHER_HOTSEAT_PREDICTION_PINNED(629),
+
+        @UiEvent(doc = "Activity to add external item was started")
+        LAUNCHER_ADD_EXTERNAL_ITEM_START(641),
+
+        @UiEvent(doc = "Activity to add external item was cancelled")
+        LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED(642),
+
+        @UiEvent(doc = "Activity to add external item was backed out")
+        LAUNCHER_ADD_EXTERNAL_ITEM_BACK(643),
+
+        @UiEvent(doc = "Item was placed automatically in external item addition flow")
+        LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY(644),
+
+        @UiEvent(doc = "Item was dragged in external item addition flow")
+        LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED(645),
+
+        @UiEvent(doc = "Undo event was tapped.")
+        LAUNCHER_UNDO(648),
+
+        @UiEvent(doc = "Task switcher clear all target was tapped.")
+        LAUNCHER_TASK_CLEAR_ALL(649),
+
+        @UiEvent(doc = "Task preview was long pressed.")
+        LAUNCHER_TASK_PREVIEW_LONGPRESS(650),
+
+        @UiEvent(doc = "User swiped down on workspace (triggering noti shade to open).")
+        LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN(651),
+
+        @UiEvent(doc = "Notification dismissed by swiping right.")
+        LAUNCHER_NOTIFICATION_DISMISSED(652),
+        ;
 
         // ADD MORE
 
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
deleted file mode 100644
index a5cc7ea..0000000
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.launcher3.logging;
-
-import android.view.View;
-import android.view.ViewParent;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-
-import java.util.ArrayList;
-
-public class StatsLogUtils {
-    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
-
-    /**
-     * Implemented by containers to provide a container source for a given child.
-     */
-    public interface LogContainerProvider {
-
-        /**
-         * Populates parent container targets for an item
-         */
-        void fillInLogContainerData(ItemInfo childInfo, Target child, ArrayList<Target> parents);
-    }
-
-    /**
-     * Recursively finds the parent of the given child which implements IconLogInfoProvider
-     */
-    public static LogContainerProvider getLaunchProviderRecursive(@Nullable View v) {
-        ViewParent parent;
-        if (v != null) {
-            parent = v.getParent();
-        } else {
-            return null;
-        }
-
-        // Optimization to only check up to 5 parents.
-        int count = MAXIMUM_VIEW_HIERARCHY_LEVEL;
-        while (parent != null && count-- > 0) {
-            if (parent instanceof LogContainerProvider) {
-                return (LogContainerProvider) parent;
-            } else {
-                parent = parent.getParent();
-            }
-        }
-        return null;
-    }
-}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
deleted file mode 100644
index a40cc26..0000000
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2012 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.logging;
-
-import static com.android.launcher3.logging.LoggerUtils.newAction;
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newDropTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
-
-import static java.util.Optional.ofNullable;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.launcher3.util.LogConfig;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-
-import java.util.ArrayList;
-import java.util.UUID;
-
-/**
- * Manages the creation of {@link LauncherEvent}.
- * To debug this class, execute following command before side loading a new apk.
- * <p>
- * $ adb shell setprop log.tag.UserEvent VERBOSE
- */
-public class UserEventDispatcher implements ResourceBasedOverride {
-
-    private static final String TAG = "UserEvent";
-    private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
-    private static final String UUID_STORAGE = "uuid";
-
-    /**
-     * A factory method for UserEventDispatcher
-     */
-    public static UserEventDispatcher newInstance(Context context) {
-        SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
-        String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
-        if (uuidStr == null) {
-            uuidStr = UUID.randomUUID().toString();
-            sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply();
-        }
-        UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
-                context.getApplicationContext(), R.string.user_event_dispatcher_class);
-        ued.mUuidStr = uuidStr;
-        ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
-        return ued;
-    }
-
-
-    /**
-     * Fills in the container data on the given event if the given view is not null.
-     *
-     * @return whether container data was added.
-     */
-    private boolean fillLogContainer(@Nullable View v, Target child,
-            @Nullable ArrayList<Target> targets) {
-        LogContainerProvider firstParent = StatsLogUtils.getLaunchProviderRecursive(v);
-        if (v == null || !(v.getTag() instanceof ItemInfo) || firstParent == null) {
-            return false;
-        }
-        final ItemInfo itemInfo = (ItemInfo) v.getTag();
-        firstParent.fillInLogContainerData(itemInfo, child, targets);
-        return true;
-    }
-
-    protected void onFillInLogContainerData(@NonNull ItemInfo itemInfo, @NonNull Target target,
-            @NonNull ArrayList<Target> targets) {
-    }
-
-    private boolean mSessionStarted;
-    private long mElapsedContainerMillis;
-    private long mElapsedSessionMillis;
-    private long mActionDurationMillis;
-    private String mUuidStr;
-    protected InstantAppResolver mInstantAppResolver;
-    private boolean mAppOrTaskLaunch;
-    private boolean mPreviousHomeGesture;
-
-    private void fillComponentInfo(Target target, ComponentName cn) {
-        if (cn != null) {
-            target.packageNameHash = (mUuidStr + cn.getPackageName()).hashCode();
-            target.componentHash = (mUuidStr + cn.flattenToString()).hashCode();
-        }
-    }
-
-    public void logActionCommand(int command, int srcContainerType, int dstContainerType) {
-        logActionCommand(command, newContainerTarget(srcContainerType),
-                dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
-    }
-
-    public void logActionCommand(int command, int srcContainerType, int dstContainerType,
-            int pageIndex) {
-        Target srcTarget = newContainerTarget(srcContainerType);
-        srcTarget.pageIndex = pageIndex;
-        logActionCommand(command, srcTarget,
-                dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
-    }
-
-    public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
-        LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
-        if (command == Action.Command.STOP) {
-            if (mAppOrTaskLaunch || !mSessionStarted) {
-                mSessionStarted = false;
-                return;
-            }
-        }
-
-        if (dstTarget != null) {
-            event.destTarget = new Target[1];
-            event.destTarget[0] = dstTarget;
-            event.action.isStateChange = true;
-        }
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionOnControl(int action, int controlType) {
-        logActionOnControl(action, controlType, null);
-    }
-
-    public void logActionOnControl(int action, int controlType, int parentContainerType) {
-        logActionOnControl(action, controlType, null, parentContainerType);
-    }
-
-    /**
-     * Logs control action with proper parent hierarchy
-     */
-    public void logActionOnControl(int actionType, int controlType,
-            @Nullable View controlInContainer, int... parentTypes) {
-        Target control = newTarget(Target.Type.CONTROL);
-        control.controlType = controlType;
-        Action action = newAction(actionType);
-
-        ArrayList<Target> targets = makeTargetsList(control);
-        if (controlInContainer != null) {
-            fillLogContainer(controlInContainer, control, targets);
-        }
-        for (int parentContainerType : parentTypes) {
-            if (parentContainerType < 0) continue;
-            targets.add(newContainerTarget(parentContainerType));
-        }
-        LauncherEvent event = newLauncherEvent(action, targets);
-        if (actionType == Action.Touch.DRAGDROP) {
-            event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
-        }
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionTapOutside(Target target) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Type.TOUCH),
-                target);
-        event.action.isOutside = true;
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionBounceTip(int containerType) {
-        LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
-                newContainerTarget(containerType));
-        event.srcTarget[0].tipType = TipType.BOUNCE;
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionOnContainer(int action, int dir, int containerType) {
-        logActionOnContainer(action, dir, containerType, 0);
-    }
-
-    public void logActionOnContainer(int action, int dir, int containerType, int pageIndex) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(action),
-                newContainerTarget(containerType));
-        event.action.dir = dir;
-        event.srcTarget[0].pageIndex = pageIndex;
-        dispatchUserEvent(event, null);
-    }
-
-    /**
-     * Used primarily for swipe up and down when state changes when swipe up happens from the
-     * navbar bezel, the {@param srcChildContainerType} is NAVBAR and
-     * {@param srcParentContainerType} is either one of the two
-     * (1) WORKSPACE: if the launcher is the foreground activity
-     * (2) APP: if another app was the foreground activity
-     */
-    public void logStateChangeAction(int action, int dir, int downX, int downY,
-            int srcChildTargetType, int srcParentContainerType, int dstContainerType,
-            int pageIndex) {
-        LauncherEvent event;
-        if (srcChildTargetType == ItemType.TASK) {
-            event = newLauncherEvent(newTouchAction(action),
-                    newItemTarget(srcChildTargetType),
-                    newContainerTarget(srcParentContainerType));
-        } else {
-            event = newLauncherEvent(newTouchAction(action),
-                    newContainerTarget(srcChildTargetType),
-                    newContainerTarget(srcParentContainerType));
-        }
-        event.destTarget = new Target[1];
-        event.destTarget[0] = newContainerTarget(dstContainerType);
-        event.action.dir = dir;
-        event.action.isStateChange = true;
-        event.srcTarget[0].pageIndex = pageIndex;
-        event.srcTarget[0].spanX = downX;
-        event.srcTarget[0].spanY = downY;
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionOnItem(int action, int dir, int itemType) {
-        logActionOnItem(action, dir, itemType, null, null);
-    }
-
-    /**
-     * Creates new {@link LauncherEvent} of ITEM target type with input arguments and dispatches it.
-     *
-     * @param touchAction ENUM value of {@link LauncherLogProto.Action.Touch} Action
-     * @param dir         ENUM value of {@link LauncherLogProto.Action.Direction} Action
-     * @param itemType    ENUM value of {@link LauncherLogProto.ItemType}
-     * @param gridX       Nullable X coordinate of item's position on the workspace grid
-     * @param gridY       Nullable Y coordinate of item's position on the workspace grid
-     */
-    public void logActionOnItem(int touchAction, int dir, int itemType,
-            @Nullable Integer gridX, @Nullable Integer gridY) {
-        Target itemTarget = newTarget(Target.Type.ITEM);
-        itemTarget.itemType = itemType;
-        ofNullable(gridX).ifPresent(value -> itemTarget.gridX = value);
-        ofNullable(gridY).ifPresent(value -> itemTarget.gridY = value);
-        LauncherEvent event = newLauncherEvent(newTouchAction(touchAction), itemTarget);
-        event.action.dir = dir;
-        dispatchUserEvent(event, null);
-    }
-
-    /**
-     * Logs proto lite version of LauncherEvent object to clearcut.
-     */
-    public void logLauncherEvent(
-            com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
-
-        if (mPreviousHomeGesture) {
-            mPreviousHomeGesture = false;
-        }
-        mAppOrTaskLaunch = false;
-        launcherEvent.toBuilder()
-                .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
-                .setElapsedSessionMillis(
-                        SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
-        try {
-            dispatchUserEvent(LauncherEvent.parseFrom(launcherEvent.toByteArray()), null);
-        } catch (InvalidProtocolBufferNanoException e) {
-            throw new RuntimeException("Cannot convert LauncherEvent from Lite to Nano version.");
-        }
-    }
-
-    public void logDeepShortcutsOpen(View icon) {
-        ItemInfo info = (ItemInfo) icon.getTag();
-        Target child = newItemTarget(info, mInstantAppResolver);
-        ArrayList<Target> targets = makeTargetsList(child);
-        fillLogContainer(icon, child, targets);
-        dispatchUserEvent(newLauncherEvent(newTouchAction(Action.Touch.TAP), targets), null);
-    }
-
-    public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
-        Target srcChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
-        ArrayList<Target> srcTargets = makeTargetsList(srcChild);
-
-
-        Target destChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
-        ArrayList<Target> destTargets = makeTargetsList(destChild);
-
-        //dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets);
-        if (dropTargetAsView instanceof LogContainerProvider) {
-            ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(dragObj.dragInfo,
-                    destChild, destTargets);
-        }
-        else {
-            destTargets.add(newDropTarget(dropTargetAsView));
-        }
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP), srcTargets);
-        Target[] destTargetsArray = new Target[destTargets.size()];
-        destTargets.toArray(destTargetsArray);
-        event.destTarget = destTargetsArray;
-
-        event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
-        dispatchUserEvent(event, null);
-    }
-
-    public final void startSession() {
-        mSessionStarted = true;
-        mElapsedSessionMillis = SystemClock.uptimeMillis();
-        mElapsedContainerMillis = SystemClock.uptimeMillis();
-    }
-
-    public final void setPreviousHomeGesture(boolean homeGesture) {
-        mPreviousHomeGesture = homeGesture;
-    }
-
-    public final boolean isPreviousHomeGesture() {
-        return mPreviousHomeGesture;
-    }
-
-    public final void resetActionDurationMillis() {
-        mActionDurationMillis = SystemClock.uptimeMillis();
-    }
-
-    public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
-        if (mPreviousHomeGesture) {
-            mPreviousHomeGesture = false;
-        }
-        mAppOrTaskLaunch = false;
-        ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
-        ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
-        if (!IS_VERBOSE) {
-            return;
-        }
-        LauncherLogProto.LauncherEvent liteLauncherEvent;
-        try {
-            liteLauncherEvent =
-                    LauncherLogProto.LauncherEvent.parseFrom(MessageNano.toByteArray(ev));
-        } catch (InvalidProtocolBufferException e) {
-            throw new RuntimeException("Cannot parse LauncherEvent from Nano to Lite version");
-        }
-        Log.d(TAG, liteLauncherEvent.toString());
-    }
-
-    /**
-     * Constructs an ArrayList with targets
-     */
-    public static ArrayList<Target> makeTargetsList(Target... targets) {
-        ArrayList<Target> result = new ArrayList<>();
-        for (Target target : targets) {
-            result.add(target);
-        }
-        return result;
-    }
-}
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 41ccbd7..06a2c92 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -20,15 +20,9 @@
 
 import static androidx.core.util.Preconditions.checkNotNull;
 
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.EMPTY_LABEL;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
 
 import android.os.Process;
 
@@ -43,10 +37,6 @@
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.userevent.LauncherLogProto;
-import com.android.launcher3.userevent.LauncherLogProto.Target;
-import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
-import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.ArrayList;
@@ -359,113 +349,4 @@
         }
         return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
     }
-
-    /**
-     * Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
-     *
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent(FromState fromState,
-            ToState toState) {
-        return LauncherLogProto.LauncherEvent.newBuilder()
-                .setAction(LauncherLogProto.Action
-                        .newBuilder()
-                        .setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
-                .addSrcTarget(Target
-                        .newBuilder()
-                        .setType(Target.Type.ITEM)
-                        .setItemType(LauncherLogProto.ItemType.EDITTEXT)
-                        .setFromFolderLabelState(convertFolderLabelState(fromState))
-                        .setToFolderLabelState(convertFolderLabelState(toState)))
-                .addSrcTarget(Target.newBuilder()
-                        .setType(Target.Type.CONTAINER)
-                        .setContainerType(LauncherLogProto.ContainerType.FOLDER)
-                        .setPageIndex(screenId)
-                        .setGridX(cellX)
-                        .setGridY(cellY)
-                        .setCardinality(contents.size()))
-                .addSrcTarget(newParentContainerTarget())
-                .build();
-    }
-
-    /**
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    private Target.Builder newParentContainerTarget() {
-        Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
-        switch (container) {
-            case CONTAINER_HOTSEAT:
-                return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
-            case CONTAINER_DESKTOP:
-                return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
-            default:
-                throw new AssertionError(String
-                        .format("Expected container to be either %s or %s but found %s.",
-                                CONTAINER_HOTSEAT,
-                                CONTAINER_DESKTOP,
-                                container));
-        }
-    }
-
-    /**
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
-        switch (fromState) {
-            case FROM_EMPTY:
-                return FROM_EMPTY;
-            case FROM_SUGGESTED:
-                return FROM_SUGGESTED;
-            case FROM_CUSTOM:
-                return FROM_CUSTOM;
-            default:
-                return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-        }
-    }
-
-    /**
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    private static ToFolderLabelState convertFolderLabelState(ToState toState) {
-        switch (toState) {
-            case UNCHANGED:
-                return ToFolderLabelState.UNCHANGED;
-            case TO_SUGGESTION0:
-                return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION1_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
-            case TO_SUGGESTION2_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
-            case TO_SUGGESTION3_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
-            case TO_EMPTY_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
-            case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
-            case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
-                return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
-            case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
-                return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
-            case TO_CUSTOM_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
-            case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
-            case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
-                return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
-            case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
-                return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
-            default:
-                return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/model/data/RemoteActionItemInfo.java b/src/com/android/launcher3/model/data/RemoteActionItemInfo.java
new file mode 100644
index 0000000..81f7f3a
--- /dev/null
+++ b/src/com/android/launcher3/model/data/RemoteActionItemInfo.java
@@ -0,0 +1,64 @@
+/*
+ * 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.model.data;
+
+import android.app.RemoteAction;
+import android.os.Process;
+
+/**
+ * Represents a launchable {@link RemoteAction}
+ */
+public class RemoteActionItemInfo extends ItemInfoWithIcon {
+
+    private final RemoteAction mRemoteAction;
+    private final String mToken;
+    private final boolean mShouldStart;
+
+    public RemoteActionItemInfo(RemoteAction remoteAction, String token, boolean shouldStart) {
+        mShouldStart = shouldStart;
+        mToken = token;
+        mRemoteAction = remoteAction;
+        title = remoteAction.getTitle();
+        user = Process.myUserHandle();
+    }
+
+    public RemoteActionItemInfo(RemoteActionItemInfo info) {
+        super(info);
+        this.mShouldStart = info.mShouldStart;
+        this.mRemoteAction = info.mRemoteAction;
+        this.mToken = info.mToken;
+    }
+
+    @Override
+    public ItemInfoWithIcon clone() {
+        return new RemoteActionItemInfo(this);
+    }
+
+    public RemoteAction getRemoteAction() {
+        return mRemoteAction;
+    }
+
+    public String getToken() {
+        return mToken;
+    }
+
+    /**
+     * Getter method for mShouldStart
+     */
+    public boolean shouldStartInLauncher() {
+        return mShouldStart;
+    }
+}
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 32f060b..9b06523 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.notification;
 
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
@@ -41,7 +42,6 @@
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Themes;
 
 /**
@@ -168,10 +168,7 @@
         Launcher launcher = Launcher.getLauncher(getContext());
         launcher.getPopupDataProvider().cancelNotification(
                 mNotificationInfo.notificationKey);
-        launcher.getUserEventDispatcher().logActionOnItem(
-                LauncherLogProto.Action.Touch.SWIPE,
-                LauncherLogProto.Action.Direction.RIGHT, // Assume all swipes are right for logging.
-                LauncherLogProto.ItemType.NOTIFICATION);
+        launcher.getStatsLogManager().logger().log(LAUNCHER_NOTIFICATION_DISMISSED);
     }
 
     // SingleAxisSwipeDetector.Listener's
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 753a6dd..fa25114 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.pm;
 
 import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -31,6 +32,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.SessionCommitReceiver;
@@ -39,10 +41,10 @@
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -65,27 +67,27 @@
 
     private final LauncherApps mLauncherApps;
     private final Context mAppContext;
-    private final IntSet mPromiseIconIds;
 
     private final PackageInstaller mInstaller;
     private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
 
+    private IntSet mPromiseIconIds;
+
     public InstallSessionHelper(Context context) {
         mInstaller = context.getPackageManager().getPackageInstaller();
         mAppContext = context.getApplicationContext();
         mLauncherApps = context.getSystemService(LauncherApps.class);
+    }
 
+    @WorkerThread
+    private IntSet getPromiseIconIds() {
+        Preconditions.assertWorkerThread();
+        if (mPromiseIconIds != null) {
+            return mPromiseIconIds;
+        }
         mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
-                getPrefs(context).getString(PROMISE_ICON_IDS, "")));
+                getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
 
-        cleanUpPromiseIconIds();
-    }
-
-    public static UserHandle getUserHandle(SessionInfo info) {
-        return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
-    }
-
-    protected void cleanUpPromiseIconIds() {
         IntArray existingIds = new IntArray();
         for (SessionInfo info : getActiveSessions().values()) {
             existingIds.add(info.getSessionId());
@@ -100,6 +102,7 @@
         for (int i = idsToRemove.size() - 1; i >= 0; --i) {
             mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
         }
+        return mPromiseIconIds;
     }
 
     public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
@@ -126,7 +129,7 @@
 
     private void updatePromiseIconPrefs() {
         getPrefs(mAppContext).edit()
-                .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+                .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
                 .apply();
     }
 
@@ -184,13 +187,15 @@
         return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
     }
 
+    @WorkerThread
     public boolean promiseIconAddedForId(int sessionId) {
-        return mPromiseIconIds.contains(sessionId);
+        return getPromiseIconIds().contains(sessionId);
     }
 
+    @WorkerThread
     public void removePromiseIconId(int sessionId) {
-        if (mPromiseIconIds.contains(sessionId)) {
-            mPromiseIconIds.getArray().removeValue(sessionId);
+        if (promiseIconAddedForId(sessionId)) {
+            getPromiseIconIds().getArray().removeValue(sessionId);
             updatePromiseIconPrefs();
         }
     }
@@ -203,6 +208,7 @@
      * - The app is not already installed
      * - A promise icon for the session has not already been created
      */
+    @WorkerThread
     void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
         if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
                 && SessionCommitReceiver.isEnabled(mAppContext)
@@ -210,25 +216,24 @@
                 && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
                 && sessionInfo.getAppIcon() != null
                 && !TextUtils.isEmpty(sessionInfo.getAppLabel())
-                && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+                && !promiseIconAddedForId(sessionInfo.getSessionId())
                 && new PackageManagerHelper(mAppContext).getApplicationInfo(
                         sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
             ItemInstallQueue.INSTANCE.get(mAppContext)
                     .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
 
-            mPromiseIconIds.add(sessionInfo.getSessionId());
+            getPromiseIconIds().add(sessionInfo.getSessionId());
             updatePromiseIconPrefs();
         }
     }
 
-    public InstallSessionTracker registerInstallTracker(
-            InstallSessionTracker.Callback callback, LooperExecutor executor) {
+    public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
         InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
 
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
-            mInstaller.registerSessionCallback(tracker, executor.getHandler());
+            mInstaller.registerSessionCallback(tracker, MODEL_EXECUTOR.getHandler());
         } else {
-            mLauncherApps.registerPackageInstallerSessionCallback(executor, tracker);
+            mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, tracker);
         }
         return tracker;
     }
@@ -240,4 +245,8 @@
             mLauncherApps.unregisterPackageInstallerSessionCallback(tracker);
         }
     }
+
+    public static UserHandle getUserHandle(SessionInfo info) {
+        return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+    }
 }
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index eb3ca73..b0b907a 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -24,8 +24,11 @@
 import android.os.UserHandle;
 import android.util.SparseArray;
 
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.util.PackageUserKey;
 
+@WorkerThread
 public class InstallSessionTracker extends PackageInstaller.SessionCallback {
 
     // Lazily initialized
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 2d7d6b0..5ade22b 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -60,6 +60,9 @@
     private void onUsersChanged(Intent intent) {
         enableAndResetCache();
         mUserChangeListeners.forEach(Runnable::run);
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
+        }
     }
 
     /**
@@ -104,9 +107,6 @@
                 mUsers = null;
                 mUserToSerialMap = null;
             }
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work profile removed", new Exception());
-            }
         }
     }
 
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index d5b32fc..90285c4 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -151,32 +151,52 @@
      * @param viewsToFlip number of views from the top to to flip in case of reverse order
      */
     protected void reorderAndShow(int viewsToFlip) {
+        setupForDisplay();
+        boolean reverseOrder = mIsAboveIcon;
+        if (reverseOrder) {
+            reverseOrder(viewsToFlip);
+        }
+        onInflationComplete(reverseOrder);
+        addArrow();
+        animateOpen();
+    }
+
+    /**
+     * Shows the popup at the desired location.
+     */
+    protected void show() {
+        setupForDisplay();
+        onInflationComplete(false);
+        addArrow();
+        animateOpen();
+    }
+
+    private void setupForDisplay() {
         setVisibility(View.INVISIBLE);
         mIsOpen = true;
         getPopupContainer().addView(this);
         orientAboutObject();
+    }
 
-        boolean reverseOrder = mIsAboveIcon;
-        if (reverseOrder) {
-            int count = getChildCount();
-            ArrayList<View> allViews = new ArrayList<>(count);
-            for (int i = 0; i < count; i++) {
-                if (i == viewsToFlip) {
-                    Collections.reverse(allViews);
-                }
-                allViews.add(getChildAt(i));
+    private void reverseOrder(int viewsToFlip) {
+        int count = getChildCount();
+        ArrayList<View> allViews = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            if (i == viewsToFlip) {
+                Collections.reverse(allViews);
             }
-            Collections.reverse(allViews);
-            removeAllViews();
-            for (int i = 0; i < count; i++) {
-                addView(allViews.get(i));
-            }
-
-            orientAboutObject();
+            allViews.add(getChildAt(i));
         }
-        onInflationComplete(reverseOrder);
+        Collections.reverse(allViews);
+        removeAllViews();
+        for (int i = 0; i < count; i++) {
+            addView(allViews.get(i));
+        }
 
-        // Add the arrow.
+        orientAboutObject();
+    }
+
+    private void addArrow() {
         final Resources res = getResources();
         final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
                 ? R.dimen.popup_arrow_horizontal_center_start
@@ -214,8 +234,6 @@
 
         mArrow.setPivotX(arrowLp.width / 2);
         mArrow.setPivotY(mIsAboveIcon ? arrowLp.height : 0);
-
-        animateOpen();
     }
 
     protected boolean isAlignedWithStart() {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 26b32b8..6d92b8b 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -19,10 +19,8 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.animation.AnimatorSet;
@@ -160,8 +158,7 @@
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             BaseDragLayer dl = getPopupContainer();
             if (!dl.isEventOverView(this, ev)) {
-                mLauncher.getUserEventDispatcher().logActionTapOutside(
-                        newContainerTarget(ContainerType.DEEPSHORTCUTS));
+                // TODO: add WW log if want to log if tap closed deep shortcut container.
                 close(true);
 
                 // We let touches on the original icon go through so that users can launch
@@ -435,7 +432,9 @@
                     // Make sure we keep the original icon hidden while it is being dragged.
                     mOriginalIcon.setVisibility(INVISIBLE);
                 } else {
-                    mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
+                    // TODO: add WW logging if want to add logging for long press on popup
+                    //  container.
+                    //  mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
                     if (!mIsAboveIcon) {
                         // Show the icon but keep the text hidden.
                         mOriginalIcon.setVisibility(VISIBLE);
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 61829c0..7c393ad 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -37,7 +37,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 @TargetApi(Build.VERSION_CODES.Q)
 public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
@@ -107,9 +106,6 @@
                     Toast.LENGTH_SHORT)
                     .show();
         }
-
-        mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
-                LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 81302ac..577fe4a 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -21,8 +21,6 @@
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -117,8 +115,6 @@
                     (WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
                             R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
             widgetsBottomSheet.populateAndShow(mItemInfo);
-            mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                    ControlType.WIDGETS_BUTTON, view);
             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
                     .log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
         }
@@ -139,8 +135,6 @@
             Rect sourceBounds = mTarget.getViewBounds(view);
             new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
-            mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                    ControlType.APPINFO_TARGET, view);
             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
                     .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
         }
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 4baecb7..f4b059d 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -306,6 +306,15 @@
             return true;
         });
         sandboxCategory.addPreference(launchAssistantTutorialPreference);
+        Preference launchSandboxModeTutorialPreference = new Preference(context);
+        launchSandboxModeTutorialPreference.setKey("launchSandboxMode");
+        launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
+        launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
+        launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
+            startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE"));
+            return true;
+        });
+        sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
     }
 
     private String toName(String action) {
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index b8a184f..fd1d965 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -15,11 +15,12 @@
  */
 package com.android.launcher3.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+
 import android.content.Context;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
  * Scale down workspace/hotseat to hint at going to either overview (on pause) or first home screen.
@@ -30,7 +31,7 @@
             | FLAG_HAS_SYS_UI_SCRIM;
 
     public HintState(int id) {
-        super(id, ContainerType.DEFAULT_CONTAINERTYPE, STATE_FLAGS);
+        super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
     }
 
     @Override
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 2a4f887..45172b5 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+
 import android.content.Context;
 import android.graphics.Rect;
 
@@ -22,7 +24,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
  * Definition for spring loaded state used during drag and drop.
@@ -35,7 +36,7 @@
             | FLAG_HIDE_BACK_BUTTON;
 
     public SpringLoadedState(int id) {
-        super(id, ContainerType.WORKSPACE, STATE_FLAGS);
+        super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 8ee5a6e..9fd53e2 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -21,6 +21,9 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
@@ -52,9 +55,6 @@
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.FlingBlockCheck;
 import com.android.launcher3.util.TouchController;
 
@@ -190,11 +190,6 @@
 
     protected abstract float initCurrentAnimation(@AnimationFlags int animComponents);
 
-    /**
-     * Returns the container that the touch started from when leaving NORMAL state.
-     */
-    protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
-
     private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
         LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
                 : reachedToState ? mToState : mFromState;
@@ -307,11 +302,11 @@
     public boolean onDrag(float displacement, MotionEvent ev) {
         if (!mIsLogContainerSet) {
             if (mStartState == ALL_APPS) {
-                mStartContainerType = ContainerType.ALLAPPS;
+                mStartContainerType = LAUNCHER_STATE_ALLAPPS;
             } else if (mStartState == NORMAL) {
-                mStartContainerType = getLogContainerTypeForNormalState(ev);
+                mStartContainerType = LAUNCHER_STATE_HOME;
             } else if (mStartState == OVERVIEW) {
-                mStartContainerType = ContainerType.TASKSWITCHER;
+                mStartContainerType = LAUNCHER_STATE_OVERVIEW;
             }
             mIsLogContainerSet = true;
         }
@@ -401,7 +396,6 @@
     @Override
     public void onDragEnd(float velocity) {
         boolean fling = mDetector.isFling(velocity);
-        final int logAction = fling ? Touch.FLING : Touch.SWIPE;
 
         boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
         if (blockedFling) {
@@ -458,7 +452,7 @@
             }
         }
 
-        mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+        mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState));
         ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
         anim.setFloatValues(startProgress, endProgress);
         maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
@@ -522,11 +516,7 @@
                 .setInterpolator(scrollInterpolatorForVelocity(velocity));
     }
 
-    protected int getDirectionForLog() {
-        return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN;
-    }
-
-    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+    protected void onSwipeInteractionCompleted(LauncherState targetState) {
         if (mAtomicComponentsController != null) {
             mAtomicComponentsController.getAnimationPlayer().end();
             mAtomicComponentsController = null;
@@ -535,18 +525,18 @@
         boolean shouldGoToTargetState = true;
         if (mPendingAnimation != null) {
             boolean reachedTarget = mToState == targetState;
-            mPendingAnimation.finish(reachedTarget, logAction);
+            mPendingAnimation.finish(reachedTarget);
             mPendingAnimation = null;
             shouldGoToTargetState = !reachedTarget;
         }
         if (shouldGoToTargetState) {
-            goToTargetState(targetState, logAction);
+            goToTargetState(targetState);
         }
     }
 
-    protected void goToTargetState(LauncherState targetState, int logAction) {
+    protected void goToTargetState(LauncherState targetState) {
         if (targetState != mStartState) {
-            logReachedState(logAction, targetState);
+            logReachedState(targetState);
         }
         if (!mLauncher.isInState(targetState)) {
             // If we're already in the target state, don't jump to it at the end of the animation in
@@ -556,24 +546,18 @@
         mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(1f).setDuration(0).start();
     }
 
-    private void logReachedState(int logAction, LauncherState targetState) {
+    private void logReachedState(LauncherState targetState) {
         // Transition complete. log the action
-        mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
-                getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
-                mStartContainerType /* e.g., hotseat */,
-                mStartState.containerType /* e.g., workspace */,
-                targetState.containerType,
-                mLauncher.getWorkspace().getCurrentPage());
         mLauncher.getStatsLogManager().logger()
-                .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
-                .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
+                .withSrcState(mStartState.statsLogOrdinal)
+                .withDstState(targetState.statsLogOrdinal)
                 .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
                         .setWorkspace(
                                 LauncherAtom.WorkspaceContainer.newBuilder()
                                         .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
                         .build())
-                .log(StatsLogManager.getLauncherAtomEvent(mStartState.containerType,
-                            targetState.containerType, mToState.ordinal > mFromState.ordinal
+                .log(StatsLogManager.getLauncherAtomEvent(mStartState.statsLogOrdinal,
+                            targetState.statsLogOrdinal, mToState.ordinal > mFromState.ordinal
                                     ? LAUNCHER_UNKNOWN_SWIPEUP
                                     : LAUNCHER_UNKNOWN_SWIPEDOWN));
     }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 4a202b6..f9dcf2d 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -24,7 +24,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
  * TouchController to switch between NORMAL and ALL_APPS state.
@@ -70,12 +69,6 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
-        return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent)
-                ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
-    }
-
-    @Override
     protected float initCurrentAnimation(@AnimationFlags int animComponents) {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 61d6f7d..d56391d 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -25,7 +25,9 @@
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 
 import android.app.AlertDialog;
+import android.app.PendingIntent;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.Process;
@@ -49,6 +51,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.testing.TestLogging;
@@ -236,6 +239,27 @@
         startAppShortcutOrInfoActivity(v, shortcut, launcher);
     }
 
+    /**
+     * Event handler for a {@link android.app.RemoteAction} click
+     *
+     */
+    public static void onClickRemoteAction(Launcher launcher,
+            RemoteActionItemInfo remoteActionInfo) {
+        try {
+            PendingIntent pendingIntent = remoteActionInfo.getRemoteAction().getActionIntent();
+            if (remoteActionInfo.shouldStartInLauncher()) {
+                launcher.startIntentSenderForResult(pendingIntent.getIntentSender(), 0, null, 0, 0,
+                        0);
+            } else {
+                pendingIntent.send();
+            }
+        } catch (PendingIntent.CanceledException | IntentSender.SendIntentException e) {
+            Toast.makeText(launcher,
+                    launcher.getResources().getText(R.string.shortcut_not_available),
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
     private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index fb02f79..17f02be 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -72,7 +72,6 @@
         out.halfPageSize = view.getNormalChildHeight() / 2;
         out.halfScreenSize = view.getMeasuredHeight() / 2;
         out.screenCenter = insets.top + view.getPaddingTop() + out.scroll + out.halfPageSize;
-        out.pageParentScale = view.getScaleY();
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 354d78d..114b75a 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -112,7 +112,6 @@
         public int halfPageSize;
         public int screenCenter;
         public int halfScreenSize;
-        public float pageParentScale;
     }
 
     class ChildBounds {
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 06479e6..5f5b2d1 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -70,7 +70,6 @@
         out.halfPageSize = view.getNormalChildWidth() / 2;
         out.halfScreenSize = view.getMeasuredWidth() / 2;
         out.screenCenter = insets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize;
-        out.pageParentScale = view.getScaleX();
     }
 
     @Override
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 0a32734..a85ae45 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -20,8 +20,10 @@
 import android.os.Process;
 
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Various different executors used in Launcher
@@ -83,4 +85,29 @@
      */
     public static final LooperExecutor MODEL_EXECUTOR =
             new LooperExecutor(createAndStartNewLooper("launcher-loader"));
+
+    /**
+     * A simple ThreadFactory to set the thread name and priority when used with executors.
+     */
+    public static class SimpleThreadFactory implements ThreadFactory {
+
+        private final int mPriority;
+        private final String mNamePrefix;
+
+        private final AtomicInteger mCount = new AtomicInteger(0);
+
+        public SimpleThreadFactory(String namePrefix, int priority) {
+            mNamePrefix = namePrefix;
+            mPriority = priority;
+        }
+
+        @Override
+        public Thread newThread(Runnable runnable) {
+            Thread t = new Thread(() -> {
+                Process.setThreadPriority(mPriority);
+                runnable.run();
+            }, mNamePrefix + mCount.incrementAndGet());
+            return t;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 52a82f8..d9a14e9 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.views;
 
-import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.Utilities.getFullDrawable;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -23,9 +22,6 @@
 import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -74,7 +70,6 @@
     private static @Nullable IconLoadResult sIconLoadResult;
 
     public static final float SHAPE_PROGRESS_DURATION = 0.10f;
-    private static final int FADE_DURATION_MS = 200;
     private static final RectF sTmpRectF = new RectF();
     private static final Object[] sTmpObjArray = new Object[1];
 
@@ -89,6 +84,9 @@
 
     private IconLoadResult mIconLoadResult;
 
+    // Draw the drawable of the BubbleTextView behind ClipIconView to reveal the built in shadow.
+    private View mBtvDrawable;
+
     private ClipIconView mClipIconView;
     private @Nullable Drawable mBadge;
 
@@ -98,7 +96,6 @@
 
     private final Rect mFinalDrawableBounds = new Rect();
 
-    private AnimatorSet mFadeAnimatorSet;
     private ListenerView mListenerView;
     private Runnable mFastFinishRunnable;
 
@@ -116,6 +113,8 @@
         mIsRtl = Utilities.isRtl(getResources());
         mListenerView = new ListenerView(context, attrs);
         mClipIconView = new ClipIconView(context, attrs);
+        mBtvDrawable = new ImageView(context, attrs);
+        addView(mBtvDrawable);
         addView(mClipIconView);
         setWillNotDraw(false);
     }
@@ -176,6 +175,7 @@
         setLayoutParams(lp);
 
         mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+        mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
     }
 
     private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
@@ -292,6 +292,8 @@
         drawable = drawable == null ? null : drawable.getConstantState().newDrawable();
         int iconOffset = getOffsetForIconBounds(l, drawable, pos);
         synchronized (iconLoadResult) {
+            iconLoadResult.btvDrawable = btvIcon == null || drawable == btvIcon
+                    ? null : btvIcon.getConstantState().newDrawable();
             iconLoadResult.drawable = drawable;
             iconLoadResult.badge = badge;
             iconLoadResult.iconOffset = iconOffset;
@@ -311,7 +313,8 @@
      * @param iconOffset The amount of offset needed to match this view with the original view.
      */
     @UiThread
-    private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge, int iconOffset) {
+    private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge,
+            @Nullable Drawable btvIcon, int iconOffset) {
         final InsettableFrameLayout.LayoutParams lp =
                 (InsettableFrameLayout.LayoutParams) getLayoutParams();
         mBadge = badge;
@@ -342,6 +345,10 @@
                 mBadge.setBounds(0, 0, clipViewOgWidth, clipViewOgHeight);
             }
         }
+
+        if (!mIsOpening && btvIcon != null) {
+            mBtvDrawable.setBackground(btvIcon);
+        }
         invalidate();
     }
 
@@ -360,7 +367,7 @@
         synchronized (mIconLoadResult) {
             if (mIconLoadResult.isIconLoaded) {
                 setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
-                        mIconLoadResult.iconOffset);
+                        mIconLoadResult.btvDrawable, mIconLoadResult.iconOffset);
                 setIconAndDotVisible(originalView, false);
             } else {
                 mIconLoadResult.onIconLoaded = () -> {
@@ -369,7 +376,7 @@
                     }
 
                     setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
-                            mIconLoadResult.iconOffset);
+                            mIconLoadResult.btvDrawable, mIconLoadResult.iconOffset);
 
                     setVisibility(VISIBLE);
                     setIconAndDotVisible(originalView, false);
@@ -434,10 +441,6 @@
             mEndRunnable.run();
             mEndRunnable = null;
         }
-        if (mFadeAnimatorSet != null) {
-            mFadeAnimatorSet.end();
-            mFadeAnimatorSet = null;
-        }
     }
 
     @Override
@@ -546,8 +549,16 @@
                     setIconAndDotVisible(originalView, true);
                     view.finish(dragLayer);
                 } else {
-                    view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
-                    view.mFadeAnimatorSet.start();
+                    originalView.setVisibility(VISIBLE);
+                    if (originalView instanceof IconLabelDotView) {
+                        setIconAndDotVisible(originalView, true);
+                    }
+                    if (originalView instanceof BubbleTextView) {
+                        BubbleTextView btv = (BubbleTextView) originalView;
+                        btv.setIconVisible(true);
+                        btv.setForceHideDot(true);
+                    }
+                    view.finish(dragLayer);
                 }
             } else {
                 view.finish(dragLayer);
@@ -564,47 +575,6 @@
         return view;
     }
 
-    private AnimatorSet createFadeAnimation(View originalView, DragLayer dragLayer) {
-        AnimatorSet fade = new AnimatorSet();
-        fade.setDuration(FADE_DURATION_MS);
-        fade.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                originalView.setVisibility(VISIBLE);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                finish(dragLayer);
-            }
-        });
-
-        if (originalView instanceof IconLabelDotView) {
-            fade.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    setIconAndDotVisible(originalView, true);
-                }
-            });
-        }
-
-        if (originalView instanceof BubbleTextView) {
-            BubbleTextView btv = (BubbleTextView) originalView;
-            fade.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    btv.setIconVisible(true);
-                    btv.setForceHideDot(true);
-                }
-            });
-            fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
-        } else if (!(originalView instanceof FolderIcon)) {
-            fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
-        }
-
-        return fade;
-    }
-
     private void finish(DragLayer dragLayer) {
         ((ViewGroup) dragLayer.getParent()).removeView(this);
         dragLayer.removeView(mListenerView);
@@ -628,11 +598,7 @@
         mLoadIconSignal = null;
         mEndRunnable = null;
         mFinalDrawableBounds.setEmpty();
-        if (mFadeAnimatorSet != null) {
-            mFadeAnimatorSet.cancel();
-        }
         mPositionOut = null;
-        mFadeAnimatorSet = null;
         mListenerView.setListener(null);
         mOriginalIcon = null;
         mOnTargetChangeRunnable = null;
@@ -640,11 +606,13 @@
         sTmpObjArray[0] = null;
         mIconLoadResult = null;
         mClipIconView.recycle();
+        mBtvDrawable.setBackground(null);
         mFastFinishRunnable = null;
     }
 
     private static class IconLoadResult {
         final ItemInfo itemInfo;
+        Drawable btvDrawable;
         Drawable drawable;
         Drawable badge;
         int iconOffset;
diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java
index a8e1c6b..dd322d9 100644
--- a/src/com/android/launcher3/views/HeroSearchResultView.java
+++ b/src/com/android/launcher3/views/HeroSearchResultView.java
@@ -16,12 +16,16 @@
 package com.android.launcher3.views;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Point;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
@@ -31,9 +35,10 @@
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
-import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.graphics.DragPreviewProvider;
@@ -53,9 +58,10 @@
  * A view representing a high confidence app search result that includes shortcuts
  */
 public class HeroSearchResultView extends LinearLayout implements DragSource,
-        AllAppsSearchBarController.PayloadResultHandler<List<ItemInfoWithIcon>> {
+        PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
 
     public static final int MAX_SHORTCUTS_COUNT = 2;
+    private final Object[] mTargetInfo = createTargetInfo();
     BubbleTextView mBubbleTextView;
     View mIconView;
     BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
@@ -102,7 +108,7 @@
                             grid.allAppsIconSizePx));
             bubbleTextView.setOnClickListener(view -> {
                 WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
-                SearchTargetEvent event = new SearchTargetEvent(
+                SearchTargetEvent event = getSearchTargetEvent(
                         SearchTarget.ItemType.APP_HERO,
                         SearchTargetEvent.CHILD_SELECT);
                 event.bundle = getAppBundle(itemInfo);
@@ -119,15 +125,25 @@
      * Apply {@link ItemInfo} for appIcon and shortcut Icons
      */
     @Override
-    public void applyAdapterInfo(AdapterItemWithPayload<List<ItemInfoWithIcon>> adapterItem) {
+    public void applyAdapterInfo(
+            AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
         mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
         mIconView.setBackground(mBubbleTextView.getIcon());
         mIconView.setTag(adapterItem.appInfo);
-        List<ItemInfoWithIcon> shorcutInfos = adapterItem.getPayload();
+        List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
+        LauncherAppState appState = LauncherAppState.getInstance(getContext());
         for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
-            mDeepShortcutTextViews[i].setVisibility(shorcutInfos.size() > i ? VISIBLE : GONE);
-            if (i < shorcutInfos.size()) {
-                mDeepShortcutTextViews[i].applyFromItemInfoWithIcon(shorcutInfos.get(i));
+            BubbleTextView shortcutView = mDeepShortcutTextViews[i];
+            mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
+            if (i < shortcutDetails.size()) {
+                Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
+                //apply ItemInfo and prepare view
+                shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
+                MODEL_EXECUTOR.execute(() -> {
+                    // load unbadged shortcut in background and update view when icon ready
+                    appState.getIconCache().getUnbadgedShortcutIcon(p.second, p.first);
+                    MAIN_EXECUTOR.post(() -> shortcutView.reapplyItemInfo(p.second));
+                });
             }
         }
         mPlugin = adapterItem.getPlugin();
@@ -135,6 +151,11 @@
     }
 
     @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
+    @Override
     public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
         mBubbleTextView.setVisibility(VISIBLE);
         mBubbleTextView.setIconVisible(true);
@@ -169,7 +190,7 @@
             mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
                     draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
 
-            SearchTargetEvent event = new SearchTargetEvent(
+            SearchTargetEvent event = mContainer.getSearchTargetEvent(
                     SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
             event.bundle = getAppBundle(itemInfo);
             if (mContainer.mPlugin != null) {
@@ -186,7 +207,7 @@
         Launcher launcher = Launcher.getLauncher(getContext());
         launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
 
-        SearchTargetEvent event = new SearchTargetEvent(
+        SearchTargetEvent event = getSearchTargetEvent(
                 SearchTarget.ItemType.APP_HERO, eventType);
         event.bundle = getAppBundle(itemInfo);
         if (mPlugin != null) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 3ec20d5..80f0981 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.views;
 
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
+import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_LAUNCH_SOURCE;
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
@@ -132,7 +133,7 @@
             view.setOnLongClickListener(popup);
             popup.mItemMap.put(view, item);
         }
-        popup.reorderAndShow(popup.getChildCount());
+        popup.show();
     }
 
     @VisibleForTesting
@@ -211,7 +212,8 @@
         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                 .putExtra(EXTRA_WALLPAPER_OFFSET,
-                        launcher.getWorkspace().getWallpaperOffsetForCenterPage());
+                        launcher.getWorkspace().getWallpaperOffsetForCenterPage())
+                .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
         if (!Utilities.existsStyleWallpapers(launcher)) {
             intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
         } else {
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
new file mode 100644
index 0000000..313ae5e
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -0,0 +1,155 @@
+/*
+ * 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.app.RemoteAction;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTarget.ItemType;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A view representing a stand alone shortcut search result
+ */
+public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
+        AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
+
+    private final Object[] mTargetInfo = createTargetInfo();
+    private ShortcutInfo mShortcutInfo;
+    private AllAppsSearchPlugin mPlugin;
+    private AdapterItemWithPayload<SearchTarget> mAdapterItem;
+
+
+    public SearchResultIconRow(@NonNull Context context) {
+        super(context);
+    }
+
+    public SearchResultIconRow(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
+        if (mAdapterItem != null) {
+            mAdapterItem.setSelectionHandler(null);
+        }
+        mAdapterItem = adapterItemWithPayload;
+        SearchTarget payload = adapterItemWithPayload.getPayload();
+        mPlugin = adapterItemWithPayload.getPlugin();
+
+        if (payload.mRemoteAction != null) {
+            prepareUsingRemoteAction(payload.mRemoteAction,
+                    payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
+                    payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
+        } else {
+            prepareUsingShortcutInfo(payload.shortcuts.get(0));
+        }
+        setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
+        adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+    }
+
+    private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
+        mShortcutInfo = shortcutInfo;
+        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
+        applyFromWorkspaceItem(workspaceItemInfo);
+        LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
+        MODEL_EXECUTOR.execute(() -> {
+            launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
+            reapplyItemInfoAsync(workspaceItemInfo);
+        });
+    }
+
+    private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) {
+        RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
+
+        applyFromRemoteActionInfo(itemInfo);
+        UI_HELPER_EXECUTOR.post(() -> {
+            // If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
+            try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
+                Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
+                itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
+                        Build.VERSION.SDK_INT);
+                reapplyItemInfoAsync(itemInfo);
+            }
+        });
+
+    }
+
+    void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
+        MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
+    }
+
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
+    private void handleSelection(int eventType) {
+        ItemInfo itemInfo = (ItemInfo) getTag();
+        Launcher launcher = Launcher.getLauncher(getContext());
+        final SearchTargetEvent searchTargetEvent;
+        if (itemInfo instanceof WorkspaceItemInfo) {
+            ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
+            searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
+                    eventType);
+            searchTargetEvent.shortcut = mShortcutInfo;
+        } else {
+            RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
+            ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
+            searchTargetEvent = getSearchTargetEvent(ItemType.ACTION,
+                    eventType);
+            searchTargetEvent.bundle = new Bundle();
+            searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
+            searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
+                    remoteItemInfo.shouldStartInLauncher());
+            searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
+                    remoteItemInfo.getToken());
+        }
+        if (mPlugin != null) {
+            mPlugin.notifySearchTargetEvent(searchTargetEvent);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java
index 6e45e88..0c9a22f 100644
--- a/src/com/android/launcher3/views/SearchResultPeopleView.java
+++ b/src/com/android/launcher3/views/SearchResultPeopleView.java
@@ -15,9 +15,6 @@
  */
 package com.android.launcher3.views;
 
-import static android.content.Intent.URI_ALLOW_UNSAFE;
-import static android.content.Intent.URI_ANDROID_APP_SCHEME;
-
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
@@ -28,7 +25,6 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.View;
@@ -50,7 +46,6 @@
 import com.android.systemui.plugins.shared.SearchTarget;
 import com.android.systemui.plugins.shared.SearchTargetEvent;
 
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 
 /**
@@ -66,8 +61,8 @@
     private TextView mTitleView;
     private ImageButton[] mProviderButtons = new ImageButton[3];
     private AllAppsSearchPlugin mPlugin;
-    private Uri mContactUri;
-
+    private Intent mIntent;
+    private final Object[] mTargetInfo = createTargetInfo();
 
     public SearchResultPeopleView(Context context) {
         this(context, null, 0);
@@ -109,7 +104,7 @@
         Bundle payload = adapterItemWithPayload.getPayload();
         mPlugin = adapterItemWithPayload.getPlugin();
         mTitleView.setText(payload.getString("title"));
-        mContactUri = payload.getParcelable("contact_uri");
+        mIntent = payload.getParcelable("intent");
         Bitmap icon = payload.getParcelable("icon");
         if (icon != null) {
             RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
@@ -125,25 +120,20 @@
         for (int i = 0; i < mProviderButtons.length; i++) {
             ImageButton button = mProviderButtons[i];
             if (providers != null && i < providers.size()) {
-                try {
-                    Bundle provider = providers.get(i);
-                    Intent intent = Intent.parseUri(provider.getString("intent_uri_str"),
-                            URI_ANDROID_APP_SCHEME | URI_ALLOW_UNSAFE);
-                    setupProviderButton(button, provider, intent);
-                    String pkg = provider.getString("package_name");
-                    UI_HELPER_EXECUTOR.post(() -> {
-                        try {
-                            ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
-                                    pkg, 0);
-                            Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
-                            MAIN_EXECUTOR.post(()-> button.setImageDrawable(appIcon));
-                        } catch (PackageManager.NameNotFoundException ignored) {
-                        }
+                Bundle provider = providers.get(i);
+                Intent intent = provider.getParcelable("intent");
+                setupProviderButton(button, provider, intent, adapterItemWithPayload);
+                String pkg = provider.getString("package_name");
+                UI_HELPER_EXECUTOR.post(() -> {
+                    try {
+                        ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
+                                pkg, 0);
+                        Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
+                        MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
+                    } catch (PackageManager.NameNotFoundException ignored) {
+                    }
 
-                    });
-                } catch (URISyntaxException ex) {
-                    button.setVisibility(GONE);
-                }
+                });
             } else {
                 button.setVisibility(GONE);
             }
@@ -151,15 +141,21 @@
         adapterItemWithPayload.setSelectionHandler(this::handleSelection);
     }
 
-    private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
+    private void setupProviderButton(ImageButton button, Bundle provider, Intent intent,
+            AllAppsGridAdapter.AdapterItem adapterItem) {
         Launcher launcher = Launcher.getLauncher(getContext());
         button.setOnClickListener(b -> {
             launcher.startActivitySafely(b, intent, null);
-            SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+            SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
                     SearchTarget.ItemType.PEOPLE,
                     SearchTargetEvent.CHILD_SELECT);
             searchTargetEvent.bundle = new Bundle();
-            searchTargetEvent.bundle.putParcelable("contact_uri", mContactUri);
+            searchTargetEvent.bundle.putParcelable("intent", mIntent);
             searchTargetEvent.bundle.putBundle("provider", provider);
             if (mPlugin != null) {
                 mPlugin.notifySearchTargetEvent(searchTargetEvent);
@@ -169,14 +165,13 @@
 
 
     private void handleSelection(int eventType) {
-        if (mContactUri != null) {
+        if (mIntent != null) {
             Launcher launcher = Launcher.getLauncher(getContext());
-            launcher.startActivitySafely(this, new Intent(Intent.ACTION_VIEW, mContactUri).setFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK), null);
-            SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
-                    SearchTarget.ItemType.PEOPLE, eventType);
+            launcher.startActivitySafely(this, mIntent, null);
+            SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
+                    eventType);
             searchTargetEvent.bundle = new Bundle();
-            searchTargetEvent.bundle.putParcelable("contact_uri", mContactUri);
+            searchTargetEvent.bundle.putParcelable("intent", mIntent);
             if (mPlugin != null) {
                 mPlugin.notifySearchTargetEvent(searchTargetEvent);
             }
diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java
index 8624609..ff3ecc8 100644
--- a/src/com/android/launcher3/views/SearchResultPlayItem.java
+++ b/src/com/android/launcher3/views/SearchResultPlayItem.java
@@ -58,6 +58,8 @@
     private String mPackageName;
     private boolean mIsInstantGame;
     private AllAppsSearchPlugin mPlugin;
+    private final Object[] mTargetInfo = createTargetInfo();
+
 
     public SearchResultPlayItem(Context context) {
         this(context, null, 0);
@@ -125,6 +127,11 @@
         });
     }
 
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
     private void showIfNecessary(TextView textView, @Nullable String string) {
         if (string == null || string.isEmpty()) {
             textView.setVisibility(GONE);
@@ -160,7 +167,7 @@
     }
 
     private void logSearchEvent(int eventType) {
-        SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+        SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
                 SearchTarget.ItemType.PLAY_RESULTS, eventType);
         searchTargetEvent.bundle = new Bundle();
         searchTargetEvent.bundle.putString("package_name", mPackageName);
diff --git a/src/com/android/launcher3/views/SearchResultShortcut.java b/src/com/android/launcher3/views/SearchResultShortcut.java
deleted file mode 100644
index 307cf34..0000000
--- a/src/com/android/launcher3/views/SearchResultShortcut.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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 static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.Context;
-import android.content.pm.ShortcutInfo;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.search.AllAppsSearchBarController;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
-import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
-
-/**
- * A view representing a stand alone shortcut search result
- */
-public class SearchResultShortcut extends FrameLayout implements
-        AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
-
-    private BubbleTextView mBubbleTextView;
-    private View mIconView;
-    private ShortcutInfo mShortcutInfo;
-    private AllAppsSearchPlugin mPlugin;
-
-    public SearchResultShortcut(@NonNull Context context) {
-        super(context);
-    }
-
-    public SearchResultShortcut(@NonNull Context context,
-            @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public SearchResultShortcut(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        Launcher launcher = Launcher.getLauncher(getContext());
-        DeviceProfile grid = launcher.getDeviceProfile();
-        mIconView = findViewById(R.id.icon);
-        ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams();
-        iconParams.height = grid.allAppsIconSizePx;
-        iconParams.width = grid.allAppsIconSizePx;
-        mBubbleTextView = findViewById(R.id.bubble_text);
-        setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
-    }
-
-    @Override
-    public void applyAdapterInfo(
-            AllAppsGridAdapter.AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
-        SearchTarget payload = adapterItemWithPayload.getPayload();
-        mPlugin = adapterItemWithPayload.getPlugin();
-        mShortcutInfo = payload.shortcuts.get(0);
-        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
-        mBubbleTextView.applyFromWorkspaceItem(workspaceItemInfo);
-        mIconView.setBackground(mBubbleTextView.getIcon());
-        LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
-        MODEL_EXECUTOR.execute(() -> {
-            launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
-            mBubbleTextView.applyFromWorkspaceItem(workspaceItemInfo);
-            mIconView.setBackground(mBubbleTextView.getIcon());
-        });
-        adapterItemWithPayload.setSelectionHandler(this::handleSelection);
-    }
-
-    private void handleSelection(int eventType) {
-        WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) mBubbleTextView.getTag();
-        ItemClickHandler.onClickAppShortcut(this, itemInfo, Launcher.getLauncher(getContext()));
-
-        SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
-                SearchTarget.ItemType.SHORTCUT, eventType);
-        searchTargetEvent.shortcut = mShortcutInfo;
-        if (mPlugin != null) {
-            mPlugin.notifySearchTargetEvent(searchTargetEvent);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/views/SearchResultSuggestRow.java b/src/com/android/launcher3/views/SearchResultSuggestRow.java
new file mode 100644
index 0000000..b5abbcc
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultSuggestRow.java
@@ -0,0 +1,131 @@
+/*
+ * 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 static com.android.systemui.plugins.shared.SearchTarget.ItemType.SUGGEST;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A view representing a fallback search suggestion row.
+ */
+public class SearchResultSuggestRow extends LinearLayout implements
+        View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
+
+    private final Object[] mTargetInfo = createTargetInfo();
+    private AllAppsSearchPlugin mPlugin;
+    private AdapterItemWithPayload<SearchTarget> mAdapterItem;
+    private TextView mTitle;
+
+
+    public SearchResultSuggestRow(@NonNull Context context) {
+        super(context);
+    }
+
+    public SearchResultSuggestRow(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SearchResultSuggestRow(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTitle = findViewById(R.id.title);
+        setOnClickListener(this);
+    }
+    @Override
+    public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
+        mAdapterItem = adapterItemWithPayload;
+        SearchTarget payload = adapterItemWithPayload.getPayload();
+        mPlugin = adapterItemWithPayload.getPlugin();
+
+        if (payload.mRemoteAction != null) {
+            RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(payload.mRemoteAction,
+                    payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
+                    payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
+            setTag(itemInfo);
+        }
+        showIfAvailable(mTitle, payload.mRemoteAction.getTitle().toString());
+        setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
+        adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+    }
+
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
+    private void handleSelection(int eventType) {
+        ItemInfo itemInfo = (ItemInfo) getTag();
+        Launcher launcher = Launcher.getLauncher(getContext());
+        if (itemInfo instanceof  RemoteActionItemInfo) return;
+
+        RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
+        ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
+        SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SUGGEST, eventType);
+        searchTargetEvent.bundle = new Bundle();
+        searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
+        searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
+                remoteItemInfo.shouldStartInLauncher());
+        searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
+                remoteItemInfo.getToken());
+
+        if (mPlugin != null) {
+            mPlugin.notifySearchTargetEvent(searchTargetEvent);
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        handleSelection(SearchTargetEvent.SELECT);
+    }
+
+    private void showIfAvailable(TextView view, @Nullable String string) {
+        System.out.println("Plugin suggest string:" + string);
+        if (TextUtils.isEmpty(string)) {
+            view.setVisibility(GONE);
+        } else {
+            System.out.println("Plugin suggest string:" + string);
+            view.setVisibility(VISIBLE);
+            view.setText(string);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java
index d439ee3..0fe0a43 100644
--- a/src/com/android/launcher3/views/SearchSectionHeaderView.java
+++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java
@@ -52,4 +52,9 @@
             setVisibility(INVISIBLE);
         }
     }
+
+    @Override
+    public Object[] getTargetInfo() {
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java
index 93bcee2..a1a0172 100644
--- a/src/com/android/launcher3/views/SearchSettingsRowView.java
+++ b/src/com/android/launcher3/views/SearchSettingsRowView.java
@@ -48,6 +48,8 @@
     private TextView mBreadcrumbsView;
     private Intent mIntent;
     private AllAppsSearchPlugin mPlugin;
+    private final Object[] mTargetInfo = createTargetInfo();
+
 
     public SearchSettingsRowView(@NonNull Context context) {
         super(context);
@@ -87,6 +89,11 @@
         adapterItemWithPayload.setSelectionHandler(this::handleSelection);
     }
 
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+
     private void showIfAvailable(TextView view, @Nullable String string) {
         if (TextUtils.isEmpty(string)) {
             view.setVisibility(GONE);
@@ -108,7 +115,7 @@
         Launcher launcher = Launcher.getLauncher(getContext());
         launcher.startActivityForResult(mIntent, 0);
 
-        SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+        SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
                 SearchTarget.ItemType.SETTINGS_ROW, eventType);
         searchTargetEvent.bundle = new Bundle();
         searchTargetEvent.bundle.putParcelable("intent", mIntent);
diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
new file mode 100644
index 0000000..bbc4773
--- /dev/null
+++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
@@ -0,0 +1,114 @@
+/*
+ * 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.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.util.AttributeSet;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.model.data.ItemInfo;
+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.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A view representing a high confidence app search result that includes shortcuts
+ */
+public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
+        implements AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
+
+    private final Object[] mTargetInfo = createTargetInfo();
+    AllAppsSearchPlugin mPlugin;
+    int mPosition;
+
+    public ThumbnailSearchResultView(Context context) {
+        super(context);
+    }
+
+    public ThumbnailSearchResultView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ThumbnailSearchResultView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    private void handleSelection(int eventType) {
+        Launcher launcher = Launcher.getLauncher(getContext());
+        ItemInfo itemInfo = (ItemInfo) getTag();
+        if (itemInfo instanceof RemoteActionItemInfo) {
+            RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
+            ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
+        } else {
+            ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
+        }
+        if (mPlugin != null) {
+            SearchTargetEvent event = getSearchTargetEvent(
+                    SearchTarget.ItemType.SCREENSHOT, eventType);
+            mPlugin.notifySearchTargetEvent(event);
+        }
+    }
+
+    @Override
+    public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItem) {
+        Launcher launcher = Launcher.getLauncher(getContext());
+        mPosition = adapterItem.position;
+
+        SearchTarget target = adapterItem.getPayload();
+        Bitmap bitmap;
+        if (target.mRemoteAction != null) {
+            RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction,
+                    target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
+                    target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
+            ItemClickHandler.onClickRemoteAction(launcher, itemInfo);
+            bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon()
+                    .loadDrawable(getContext())).getBitmap();
+            setTag(itemInfo);
+        } else {
+            bitmap = (Bitmap) target.bundle.getParcelable("bitmap");
+            WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+            itemInfo.intent = new Intent(Intent.ACTION_VIEW)
+                    .setData(Uri.parse(target.bundle.getString("uri")))
+                    .setType("image/*")
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            setTag(itemInfo);
+        }
+        RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(null, bitmap);
+        drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
+        setImageDrawable(drawable);
+        setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
+        mPlugin = adapterItem.getPlugin();
+        adapterItem.setSelectionHandler(this::handleSelection);
+    }
+
+    @Override
+    public Object[] getTargetInfo() {
+        return mTargetInfo;
+    }
+}