Merge changes I9ccb4335,I0d2f9be8 into sc-dev

* changes:
  Add OnComputeInsetsListener to TaskbarContainerView
  Use SYSTEM_APPLICATION_OVERLAY instead of SYSTEM_ALERT_WINDOW for Taskbar
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index cbe0eb0..edcd0a2 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -355,10 +355,6 @@
         // populating workspace.
         // TODO: Find a better place for this
         WellbeingModel.INSTANCE.get(this);
-
-        if (mTaskbarController != null) {
-            mTaskbarController.onHotseatUpdated();
-        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 034d51f..588d676 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -56,17 +56,22 @@
         return mHandler;
     }
 
-    // Called only in R+ platform
+    // Called only in S+ platform
     @BinderThread
-    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+    public void onAnimationStart(
+            int transit,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            Runnable runnable) {
         Runnable r = () -> {
             finishExistingAnimation();
             mAnimationResult = new AnimationResult(() -> {
                 UI_HELPER_EXECUTOR.execute(runnable);
                 mAnimationResult = null;
             });
-            onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
+            onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+                    mAnimationResult);
         };
         if (mStartAtFrontOfQueue) {
             postAtFrontOfQueueAsynchronously(mHandler, r);
@@ -75,6 +80,14 @@
         }
     }
 
+    // Called only in R platform
+    @BinderThread
+    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+        onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
+                new RemoteAnimationTargetCompat[0], runnable);
+    }
+
     // Called only in Q platform
     @BinderThread
     @Deprecated
@@ -88,8 +101,11 @@
      */
     @UiThread
     public abstract void onCreateAnimation(
+            int transit,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            AnimationResult result);
 
     @UiThread
     private void finishExistingAnimation() {
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 876cabc..c4b6961 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -866,8 +866,10 @@
         }
 
         @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
                 RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             if (mLauncher.isDestroyed()) {
                 AnimatorSet anim = new AnimatorSet();
@@ -880,7 +882,8 @@
                 // If launcher is not resumed, wait until new async-frame after resume
                 mLauncher.addOnResumeCallback(() ->
                         postAsyncCallback(mHandler, () ->
-                                onCreateAnimation(appTargets, wallpaperTargets, result)));
+                                onCreateAnimation(transit, appTargets, wallpaperTargets,
+                                        nonAppTargets, result)));
                 return;
             }
 
@@ -964,8 +967,10 @@
         }
 
         @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
                 RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             AnimatorSet anim = new AnimatorSet();
 
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
index da2aee4..03cc28e 100644
--- a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -26,7 +26,9 @@
  */
 public interface WrappedAnimationRunnerImpl {
     Handler getHandler();
-    void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+    void onCreateAnimation(int transit,
+            RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
             LauncherAnimationRunner.AnimationResult result);
 }
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
index 1753b62..1e1631b 100644
--- a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -46,11 +46,15 @@
     }
 
     @Override
-    public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+    public void onCreateAnimation(int transit,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            AnimationResult result) {
         R animationRunnerImpl = mImpl.get();
         if (animationRunnerImpl != null) {
-            animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
+            animationRunnerImpl.onCreateAnimation(transit, appTargets, wallpaperTargets,
+                    nonAppTargets, result);
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index aa6601b..88cfacb 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -70,7 +71,11 @@
  */
 public class HotseatPredictionController implements DragController.DragListener,
         SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
-        DragSource {
+        DragSource, ViewGroup.OnHierarchyChangeListener {
+
+    private static final int FLAG_UPDATE_PAUSED = 1 << 0;
+    private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
+    private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
 
     private int mHotSeatItemsCount;
 
@@ -80,8 +85,7 @@
     private List<ItemInfo> mPredictedItems = Collections.emptyList();
 
     private AnimatorSet mIconRemoveAnimators;
-    private boolean mUIUpdatePaused = false;
-    private boolean mDragInProgress = false;
+    private int mPauseFlags = 0;
 
     private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
 
@@ -114,6 +118,30 @@
         mLauncher.getDragController().addDragListener(this);
 
         launcher.getDeviceProfile().inv.addOnChangeListener(this);
+        mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
+    }
+
+    @Override
+    public void onChildViewAdded(View parent, View child) {
+        onHotseatHierarchyChanged();
+    }
+
+    @Override
+    public void onChildViewRemoved(View parent, View child) {
+        onHotseatHierarchyChanged();
+    }
+
+    private void onHotseatHierarchyChanged() {
+        if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+            // Post update after a single frame to avoid layout within layout
+            MAIN_EXECUTOR.getHandler().post(this::updateFillIfNotLoading);
+        }
+    }
+
+    private void updateFillIfNotLoading() {
+        if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+            fillGapsWithPrediction(true);
+        }
     }
 
     /**
@@ -160,11 +188,11 @@
     }
 
     private void fillGapsWithPrediction() {
-        fillGapsWithPrediction(false, null);
+        fillGapsWithPrediction(false);
     }
 
-    private void fillGapsWithPrediction(boolean animate, Runnable callback) {
-        if (mUIUpdatePaused || mDragInProgress) {
+    private void fillGapsWithPrediction(boolean animate) {
+        if (mPauseFlags != 0) {
             return;
         }
 
@@ -175,12 +203,14 @@
             mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
-                    fillGapsWithPrediction(animate, callback);
+                    fillGapsWithPrediction(animate);
                     mIconRemoveAnimators.removeListener(this);
                 }
             });
             return;
         }
+
+        mPauseFlags |= FLAG_FILL_IN_PROGRESS;
         for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
             View child = mHotseat.getChildAt(
                     mHotseat.getCellXFromOrder(rank),
@@ -207,10 +237,12 @@
             }
             preparePredictionInfo(predictedItem, rank);
         }
-        bindItems(newItems, animate, callback);
+        bindItems(newItems, animate);
+
+        mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
     }
 
-    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate, Runnable callback) {
+    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
         AnimatorSet animationSet = new AnimatorSet();
         for (WorkspaceItemInfo item : itemsToAdd) {
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
@@ -221,12 +253,11 @@
             }
         }
         if (animate) {
-            if (callback != null) {
-                animationSet.addListener(AnimationSuccessListener.forRunnable(callback));
-            }
+            animationSet.addListener(AnimationSuccessListener
+                    .forRunnable(this::removeOutlineDrawings));
             animationSet.start();
         } else {
-            if (callback != null) callback.run();
+            removeOutlineDrawings();
         }
 
         if (mLauncher.getTaskbarController() != null) {
@@ -234,6 +265,16 @@
         }
     }
 
+    private void removeOutlineDrawings() {
+        if (mOutlineDrawings.isEmpty()) return;
+        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+        }
+        mHotseat.invalidate();
+        mOutlineDrawings.clear();
+    }
+
+
     /**
      * Unregisters callbacks and frees resources
      */
@@ -245,11 +286,9 @@
      * start and pauses predicted apps update on the hotseat
      */
     public void setPauseUIUpdate(boolean paused) {
-        if (mLauncher.getTaskbarController() != null) {
-            // Taskbar is present, always allow updates since hotseat is still visible.
-            return;
-        }
-        mUIUpdatePaused = paused;
+        mPauseFlags = paused
+                ? (mPauseFlags | FLAG_UPDATE_PAUSED)
+                : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
         if (!paused) {
             fillGapsWithPrediction();
         }
@@ -365,14 +404,14 @@
         for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
             mHotseat.addDelegatedCellDrawing(outlineDrawing);
         }
-        mDragInProgress = true;
+        mPauseFlags |= FLAG_DRAG_IN_PROGRESS;
         mHotseat.invalidate();
     }
 
     @Override
     public void onDragEnd() {
-        mDragInProgress = false;
-        fillGapsWithPrediction(true, this::removeOutlineDrawings);
+        mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS;
+        fillGapsWithPrediction(true);
     }
 
     @Nullable
@@ -393,15 +432,6 @@
         itemInfo.screenId = rank;
     }
 
-    private void removeOutlineDrawings() {
-        if (mOutlineDrawings.isEmpty()) return;
-        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
-            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
-        }
-        mHotseat.invalidate();
-        mOutlineDrawings.clear();
-    }
-
     @Override
     public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
         this.mHotSeatItemsCount = profile.numHotseatIcons;
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
index 425e557..71270cc 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
@@ -73,11 +73,9 @@
     }
 
 
-    private void close(boolean animate, boolean markAsSeen) {
-        handleClose(animate);
-        if (markAsSeen) {
-            mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN);
-        }
+    private void dismiss() {
+        handleClose(true);
+        mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN);
     }
 
     @Override
@@ -110,7 +108,7 @@
 
         findViewById(R.id.dismiss_edu).setOnClickListener((view) -> {
             mSwitchFocusOnDismiss = true;
-            close(true, true);
+            dismiss();
         });
     }
 
@@ -176,7 +174,7 @@
 
     @Override
     public void onStateTransitionStart(LauncherState toState) {
-        close(true, false);
+        dismiss();
     }
 
     @Override
@@ -203,7 +201,7 @@
         if (mSearchInput != null) {
             mSearchInput.setText(charSequence.toString());
             mSwitchFocusOnDismiss = true;
-            close(true, true);
+            dismiss();
         }
     }
 
@@ -215,7 +213,7 @@
     @Override
     public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
         mSearchInput.onEditorAction(i);
-        close(true, true);
+        dismiss();
         return true;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
index c441e22..4bf3432 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
@@ -15,28 +15,22 @@
  */
 package com.android.launcher3.search;
 
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
 import android.app.search.SearchTarget;
 import android.app.search.SearchTargetEvent;
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.lifecycle.LiveData;
-import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 import androidx.slice.widget.EventInfo;
 import androidx.slice.widget.SliceView;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.SafeCloseable;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,13 +41,11 @@
 public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler,
         SliceView.OnSliceActionListener {
 
-    private static final String TAG = "SearchSliceController";
-
     private final Launcher mLauncher;
 
     private SliceView mSliceView;
     private SearchResultIcon mIcon;
-    private LiveData<Slice> mSliceLiveData;
+    private SafeCloseable mSliceSession;
     private String mTargetId;
 
     public SearchResultIconSlice(Context context) {
@@ -87,26 +79,20 @@
         mTargetId = parentTarget.getId();
         reset();
         updateIcon(parentTarget, children);
-        try {
-            mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri(
-                    parentTarget.getSliceUri());
-            mSliceLiveData.observe(mLauncher, mSliceView);
-        } catch (Exception ex) {
-            Log.e(TAG, "unable to bind slice", ex);
-        }
+        mSliceSession = mLauncher.getLiveSearchManager()
+                .addObserver(parentTarget.getSliceUri(), mSliceView);
     }
 
     private void updateIcon(SearchTarget parentTarget, List<SearchTarget> children) {
         if (children.size() == 1) {
             mIcon.apply(children.get(0), new ArrayList<>());
         } else {
-            LauncherAppState appState = LauncherAppState.getInstance(getContext());
-            MODEL_EXECUTOR.post(() -> {
-                PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
-                pkgItem.user = parentTarget.getUserHandle();
-                appState.getIconCache().getTitleAndIconForApp(pkgItem, false);
-                MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem));
-            });
+            PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
+            pkgItem.user = parentTarget.getUserHandle();
+            if (!pkgItem.equals(mIcon.getTag())) {
+                // The icon will load and apply high res icon automatically
+                mIcon.applyFromItemInfoWithIcon(pkgItem);
+            }
         }
     }
 
@@ -124,8 +110,8 @@
 
     private void reset() {
         mSliceView.setOnSliceActionListener(null);
-        if (mSliceLiveData != null) {
-            mSliceLiveData.removeObservers(mLauncher);
+        if (mSliceSession != null) {
+            mSliceSession.close();
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
index 4c64265..e22f6ab 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.search;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
 import android.app.search.SearchTarget;
 import android.app.search.SearchTargetEvent;
 import android.appwidget.AppWidgetHostView;
@@ -36,16 +39,19 @@
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.search.SearchWidgetInfoContainer;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
 import java.util.List;
 
-
 /**
  * displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search
  * provider
@@ -63,6 +69,7 @@
     private final float mScaleToFit;
 
     private SearchWidgetInfoContainer mInfoContainer;
+    private HandlerRunnable mLabelRequest;
     private BubbleTextView mWidgetProvider;
     private TextView mWidgetLabel;
 
@@ -124,11 +131,18 @@
     }
 
     private void showWidgetInfo(AppWidgetProviderInfo providerInfo) {
-        String title = providerInfo.loadLabel(mLauncher.getPackageManager());
         PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName());
         pinfo.user = providerInfo.getProfile();
         mWidgetProvider.applyFromItemInfoWithIcon(pinfo);
-        mWidgetLabel.setText(title);
+
+        mLabelRequest = new HandlerRunnable<>(
+                MODEL_EXECUTOR.getHandler(),
+                () -> LauncherAppState.getInstance(mLauncher).getIconCache()
+                        .getTitleNoCache(LauncherAppWidgetProviderInfo
+                                .fromProviderInfo(mLauncher, providerInfo)),
+                MAIN_EXECUTOR,
+                mWidgetLabel::setText);
+        Utilities.postAsyncCallback(MODEL_EXECUTOR.getHandler(), mLabelRequest);
     }
 
     /**
@@ -137,7 +151,18 @@
     public void removeListener() {
         if (mInfoContainer != null) {
             mInfoContainer.detachWidget(mHostView);
+            mInfoContainer = null;
         }
+        if (mLabelRequest != null) {
+            mLabelRequest.cancel();
+            mLabelRequest = null;
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        removeListener();
     }
 
     private void reportEvent(int eventType) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ee00ed2..9a1e707 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
 
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -54,6 +55,7 @@
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.search.DeviceSearchAdapterProvider;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -82,6 +84,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Stream;
@@ -164,7 +167,8 @@
     @Override
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.setPauseUIUpdate(true);
+            // Only pause is taskbar controller is not present
+            mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
         }
         return super.startActivitySafely(v, intent, item);
     }
@@ -230,6 +234,15 @@
     }
 
     @Override
+    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+        super.bindWorkspaceItemsChanged(updated);
+        if (getTaskbarController() != null && updated.stream()
+                .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
+            getTaskbarController().onHotseatUpdated();
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         getAppsView().getSearchUiManager().destroy();
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 7beeae2..8aa0842 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -184,8 +184,11 @@
             }
 
             @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+            public void onCreateAnimation(int transit,
+                    RemoteAnimationTargetCompat[] appTargets,
+                    RemoteAnimationTargetCompat[] wallpaperTargets,
+                    RemoteAnimationTargetCompat[] nonAppTargets,
+                    AnimationResult result) {
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets);
                 anim.addListener(resetStateListener());
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ca55468..85b21e0 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
+import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,15 +25,18 @@
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
@@ -431,4 +435,84 @@
             }
         }
     }
+
+    @Override
+    public void registerSplitScreenListener(ISplitScreenListener listener) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.registerSplitScreenListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call registerSplitScreenListener");
+            }
+        }
+    }
+
+    @Override
+    public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.unregisterSplitScreenListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call unregisterSplitScreenListener");
+            }
+        }
+    }
+
+    @Override
+    public void setSideStageVisibility(boolean visible) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.setSideStageVisibility(visible);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setSideStageVisibility");
+            }
+        }
+    }
+
+    @Override
+    public void exitSplitScreen() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.exitSplitScreen();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call exitSplitScreen");
+            }
+        }
+    }
+
+    @Override
+    public void startTask(int taskId, int stage, int position, Bundle options) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.startTask(taskId, stage, position, options);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTask");
+            }
+        }
+    }
+
+    @Override
+    public void startShortcut(String packageName, String shortcutId, int stage, int position,
+            Bundle options, UserHandle user) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.startShortcut(packageName, shortcutId, stage, position, options,
+                        user);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startShortcut");
+            }
+        }
+    }
+
+    @Override
+    public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.startIntent(intent, stage, position, options);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startIntent");
+            }
+        }
+    }
+
 }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 19c6588..3adb459 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -43,8 +43,11 @@
             }
 
             @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+            public void onCreateAnimation(int transit,
+                    RemoteAnimationTargetCompat[] appTargets,
+                    RemoteAnimationTargetCompat[] wallpaperTargets,
+                    RemoteAnimationTargetCompat[] nonApps,
+                    AnimationResult result) {
                 result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
             }
         };
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
new file mode 100644
index 0000000..b8600a6
--- /dev/null
+++ b/res/values-v31/colors.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2021, 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.
+*/
+-->
+<resources>
+    <color name="popup_color_primary_light">@android:color/system_main_50</color>
+    <color name="popup_color_secondary_light">@android:color/system_main_100</color>
+    <color name="popup_color_tertiary_light">@android:color/system_main_300</color>
+    <color name="popup_color_primary_dark">@android:color/system_main_800</color>
+    <color name="popup_color_secondary_dark">@android:color/system_main_900</color>
+    <color name="popup_color_tertiary_dark">@android:color/system_main_700</color>
+
+    <color name="workspace_text_color_light">@android:color/system_main_50</color>
+    <color name="workspace_text_color_dark">@android:color/system_main_900</color>
+
+    <color name="text_color_primary_dark">@android:color/system_main_50</color>
+    <color name="text_color_secondary_dark">@android:color/system_main_200</color>
+    <color name="text_color_tertiary_dark">@android:color/system_main_400</color>
+</resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 78c2df6..0b30253 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -40,4 +40,19 @@
     <color name="gesture_tutorial_fake_previous_task_view_color">#9CCC65</color> <!-- Light Green -->
     <color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
     <color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
+
+    <color name="popup_color_primary_light">#FFF</color>
+    <color name="popup_color_secondary_light">#F1F3F4</color>
+    <color name="popup_color_tertiary_light">#E0E0E0</color> <!-- Gray 300 -->
+    <color name="popup_color_primary_dark">#3C4043</color> <!-- Gray 800 -->
+    <color name="popup_color_secondary_dark">#202124</color>
+    <color name="popup_color_tertiary_dark">#757575</color> <!-- Gray 600 -->
+
+    <color name="workspace_text_color_light">#FFF</color>
+    <color name="workspace_text_color_dark">#FF212121</color>
+
+    <color name="text_color_primary_dark">#FFFFFFFF</color>
+    <color name="text_color_secondary_dark">#FFFFFFFF</color>
+    <color name="text_color_tertiary_dark">#CCFFFFFF</color>
+
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dc7182f..adc2238 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,12 +35,12 @@
         <item name="allAppsInterimScrimAlpha">46</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
         <item name="allAppsTheme">@style/AllAppsTheme</item>
-        <item name="popupColorPrimary">#FFF</item>
-        <item name="popupColorSecondary">#F1F3F4</item>
-        <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+        <item name="popupColorPrimary">@color/popup_color_primary_light</item>
+        <item name="popupColorSecondary">@color/popup_color_secondary_light</item>
+        <item name="popupColorTertiary">@color/popup_color_tertiary_light</item>
         <item name="isMainColorDark">false</item>
         <item name="isWorkspaceDarkText">false</item>
-        <item name="workspaceTextColor">@android:color/white</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_light</item>
         <item name="workspaceShadowColor">#B0000000</item>
         <item name="workspaceAmbientShadowColor">#33000000</item>
         <item name="workspaceKeyShadowColor">#44000000</item>
@@ -74,7 +74,7 @@
     </style>
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
-        <item name="workspaceTextColor">#FF212121</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="allAppsInterimScrimAlpha">128</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
@@ -88,9 +88,9 @@
     </style>
 
     <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
-        <item name="android:textColorPrimary">#FFFFFFFF</item>
-        <item name="android:textColorSecondary">#FFFFFFFF</item>
-        <item name="android:textColorTertiary">#CCFFFFFF</item>
+        <item name="android:textColorPrimary">@color/text_color_primary_dark</item>
+        <item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
+        <item name="android:textColorTertiary">@color/text_color_tertiary_dark</item>
         <item name="android:textColorHint">#A0FFFFFF</item>
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
         <item name="android:colorPrimary">#FF212121</item>
@@ -98,9 +98,9 @@
         <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="allAppsTheme">@style/AllAppsTheme.Dark</item>
-        <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
-        <item name="popupColorSecondary">#202124</item>
-        <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+        <item name="popupColorPrimary">@color/popup_color_primary_dark</item>
+        <item name="popupColorSecondary">@color/popup_color_secondary_dark</item>
+        <item name="popupColorTertiary">@color/popup_color_tertiary_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">?android:attr/colorPrimary</item>
         <item name="folderFillColor">?android:attr/colorBackground</item>
@@ -125,7 +125,7 @@
         <item name="allAppsInterimScrimAlpha">25</item>
         <item name="folderFillColor">#CDFFFFFF</item>
         <item name="folderTextColor">?attr/workspaceTextColor</item>
-        <item name="workspaceTextColor">#FF212121</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 6f446ad..21297c9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -66,9 +66,9 @@
 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;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -171,7 +171,7 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDisableRelayout = false;
 
-    private IconLoadRequest mIconLoadRequest;
+    private HandlerRunnable mIconLoadRequest;
 
     private boolean mEnableIconUpdateAnimation = false;
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2334267..fe462f7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -98,13 +98,9 @@
 import android.widget.Toast;
 
 import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -211,8 +207,7 @@
  * Default launcher application.
  */
 public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
-        Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin>,
-        LifecycleOwner {
+        Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
     public static final String TAG = "Launcher";
 
     public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -275,8 +270,6 @@
     private LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
-    private LifecycleRegistry mLifecycleRegistry;
-
     private LiveSearchManager mLiveSearchManager;
 
     @Thunk
@@ -392,12 +385,12 @@
         mIconCache = app.getIconCache();
         mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
 
-        mLiveSearchManager = new LiveSearchManager(this);
-
         mDragController = new DragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new StateManager<>(this, NORMAL);
 
+        mLiveSearchManager = new LiveSearchManager(this);
+
         mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
 
         mAppWidgetManager = new WidgetManagerHelper(this);
@@ -486,15 +479,6 @@
         if (Utilities.ATLEAST_R) {
             getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
         }
-
-        mLifecycleRegistry = new LifecycleRegistry(this);
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
-    }
-
-    @NonNull
-    @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycleRegistry;
     }
 
     public LiveSearchManager getLiveSearchManager() {
@@ -913,7 +897,6 @@
 
     @Override
     protected void onStop() {
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
         super.onStop();
         if (mDeferOverlayCallbacks) {
             checkIfOverlayStillDeferred();
@@ -922,7 +905,7 @@
         }
 
         logStopAndResume(false /* isResume */);
-        mAppWidgetHost.setListenIfResumed(false);
+        mAppWidgetHost.setActivityStarted(false);
         NotificationListener.removeNotificationsChangedListener();
     }
 
@@ -935,9 +918,8 @@
             mOverlayManager.onActivityStarted(this);
         }
 
-        mAppWidgetHost.setListenIfResumed(true);
+        mAppWidgetHost.setActivityStarted(true);
         TraceHelper.INSTANCE.endSection(traceToken);
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
     }
 
     @Override
@@ -956,6 +938,7 @@
         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
         DiscoveryBounce.showForHomeIfNeeded(this);
+        mAppWidgetHost.setActivityResumed(true);
     }
 
     private void logStopAndResume(boolean isResume) {
@@ -1049,7 +1032,7 @@
     @Override
     public void onStateSetEnd(LauncherState state) {
         super.onStateSetEnd(state);
-        getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+        getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
         getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
 
         finishAutoCancelActionMode();
@@ -1091,7 +1074,6 @@
         }
 
         TraceHelper.INSTANCE.endSection(traceToken);
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
     }
 
     @Override
@@ -1099,7 +1081,6 @@
         // Ensure that items added to Launcher are queued until Launcher returns
         ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
 
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
         super.onPause();
         mDragController.cancelDrag();
         mLastTouchUpTime = -1;
@@ -1108,6 +1089,7 @@
         if (!mDeferOverlayCallbacks) {
             mOverlayManager.onActivityPaused(this);
         }
+        mAppWidgetHost.setActivityResumed(false);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1598,7 +1580,6 @@
         mAppTransitionManager.unregisterRemoteAnimations();
         mAppTransitionManager.unregisterRemoteTransitions();
         mUserChangedCallbackCloseable.close();
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
         mLiveSearchManager.stop();
     }
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7ea6851..fea26df 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -49,8 +49,11 @@
 public class LauncherAppWidgetHost extends AppWidgetHost {
 
     private static final int FLAG_LISTENING = 1;
-    private static final int FLAG_RESUMED = 1 << 1;
-    private static final int FLAG_LISTEN_IF_RESUMED = 1 << 2;
+    private static final int FLAG_STATE_IS_NORMAL = 1 << 1;
+    private static final int FLAG_ACTIVITY_STARTED = 1 << 2;
+    private static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
+    private static final int FLAGS_SHOULD_LISTEN =
+            FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
 
     public static final int APPWIDGET_HOST_ID = 1024;
 
@@ -59,7 +62,7 @@
     private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
 
     private final Context mContext;
-    private int mFlags = FLAG_RESUMED;
+    private int mFlags = FLAG_STATE_IS_NORMAL;
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
@@ -130,49 +133,45 @@
     }
 
     /**
-     * Updates the resumed state of the host.
-     * When a host is not resumed, it defers calls to startListening until host is resumed again.
-     * But if the host was already listening, it will not call stopListening.
-     *
-     * @see #setListenIfResumed(boolean)
+     * Sets or unsets a flag the can change whether the widget host should be in the listening
+     * state.
      */
-    public void setResumed(boolean isResumed) {
-        if (isResumed == ((mFlags & FLAG_RESUMED) != 0)) {
-            return;
-        }
-        if (isResumed) {
-            mFlags |= FLAG_RESUMED;
-            // Start listening if we were supposed to start listening on resume
-            if ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0 && (mFlags & FLAG_LISTENING) == 0) {
-                startListening();
-            }
+    private void setShouldListenFlag(int flag, boolean on) {
+        if (on) {
+            mFlags |= flag;
         } else {
-            mFlags &= ~FLAG_RESUMED;
+            mFlags &= ~flag;
+        }
+
+        final boolean listening = isListening();
+        if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) {
+            // Postpone starting listening until all flags are on.
+            startListening();
+        } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
+            // Postpone stopping listening until the activity is stopped.
+            stopListening();
         }
     }
 
     /**
-     * Updates the listening state of the host. If the host is not resumed, startListening is
-     * deferred until next resume.
-     *
-     * @see #setResumed(boolean)
+     * Registers an "entering/leaving Normal state" event.
      */
-    public void setListenIfResumed(boolean listenIfResumed) {
-        if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
-            return;
-        }
-        if (listenIfResumed) {
-            mFlags |= FLAG_LISTEN_IF_RESUMED;
-            if ((mFlags & FLAG_RESUMED) != 0) {
-                // If we are resumed, start listening immediately. Note we do not check for
-                // duplicate calls before calling startListening as startListening is safe to call
-                // multiple times.
-                startListening();
-            }
-        } else {
-            mFlags &= ~FLAG_LISTEN_IF_RESUMED;
-            stopListening();
-        }
+    public void setStateIsNormal(boolean isNormal) {
+        setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal);
+    }
+
+    /**
+     * Registers an "activity started/stopped" event.
+     */
+    public void setActivityStarted(boolean isStarted) {
+        setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted);
+    }
+
+    /**
+     * Registers an "activity paused/resumed" event.
+     */
+    public void setActivityResumed(boolean isResumed) {
+        setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed);
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2f805fd..a4e1f27 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -268,13 +268,10 @@
                 && !FeatureFlags.DISABLE_INITIAL_IME_IN_ALLAPPS.get() && BuildCompat.isAtLeastR()) {
             mInsetController.onAnimationEnd(mProgress);
             if (Float.compare(mProgress, 0f) == 0) {
-                mLauncher.getLiveSearchManager().start();
                 EditText editText = mAppsView.getSearchUiManager().getEditText();
                 if (editText != null && !mInsetController.showSearchEduIfNecessary()) {
                     editText.requestFocus();
                 }
-            } else {
-                mLauncher.getLiveSearchManager().stop();
             }
             // TODO: should make the controller hide synchronously
         }
diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
index c2f0b96..e52c790 100644
--- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java
+++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
@@ -15,8 +15,16 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
 
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
@@ -26,39 +34,52 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.Log;
 
-import androidx.lifecycle.LiveData;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Observer;
 import androidx.slice.Slice;
-import androidx.slice.widget.SliceLiveData;
+import androidx.slice.SliceViewManager;
+import androidx.slice.SliceViewManager.SliceCallback;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Optional;
 
 /**
  * Manages Lifecycle for Live search results
  */
-public class LiveSearchManager {
+public class LiveSearchManager implements StateListener<LauncherState> {
+
+    private static final String TAG = "LiveSearchManager";
 
     public static final int SEARCH_APPWIDGET_HOST_ID = 2048;
 
     private final Launcher mLauncher;
-    private final AppWidgetManager mAppWidgetManger;
+    private final HashMap<Uri, SliceLifeCycle> mUriSliceMap = new HashMap<>();
+
     private final HashMap<ComponentKey, SearchWidgetInfoContainer> mWidgetPlaceholders =
             new HashMap<>();
-    private final HashMap<Uri, LiveData<Slice>> mUriSliceMap = new HashMap<>();
     private SearchWidgetHost mSearchWidgetHost;
     private InstanceId mLogInstanceId;
+    private LauncherState mPrevLauncherState;
 
     public LiveSearchManager(Launcher launcher) {
         mLauncher = launcher;
-        mAppWidgetManger = AppWidgetManager.getInstance(launcher);
+        mLauncher.getStateManager().addStateListener(this);
     }
 
     /**
@@ -67,78 +88,94 @@
      */
     public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) {
         if (mSearchWidgetHost == null) {
-            throw new RuntimeException("AppWidgetHost has not been created yet");
+            mSearchWidgetHost = new SearchWidgetHost(mLauncher);
+            mSearchWidgetHost.startListening();
         }
 
         ComponentName provider = providerInfo.provider;
         UserHandle userHandle = providerInfo.getProfile();
 
         ComponentKey key = new ComponentKey(provider, userHandle);
-        SearchWidgetInfoContainer view = mWidgetPlaceholders.getOrDefault(key, null);
         if (mWidgetPlaceholders.containsKey(key)) {
             return mWidgetPlaceholders.get(key);
         }
+
         LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
                 mLauncher, providerInfo);
         PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo);
 
         Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo);
         int appWidgetId = mSearchWidgetHost.allocateAppWidgetId();
-        boolean success = mAppWidgetManger.bindAppWidgetIdIfAllowed(appWidgetId, userHandle,
-                provider, options);
+        boolean success = AppWidgetManager.getInstance(mLauncher)
+                .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options);
         if (!success) {
+            mSearchWidgetHost.deleteAppWidgetId(appWidgetId);
             mWidgetPlaceholders.put(key, null);
             return null;
         }
 
-        view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(mLauncher, appWidgetId,
-                providerInfo);
+        SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(
+                mLauncher, appWidgetId, providerInfo);
         view.setTag(pendingAddWidgetInfo);
         mWidgetPlaceholders.put(key, view);
         return view;
     }
 
     /**
-     * Creates {@link LiveData<Slice>} from Slice Uri. Caches created live data to be reused
-     * within the same search session. Removes previous observers when new SliceView request a
-     * live data for observation.
-     */
-    public LiveData<Slice> getSliceForUri(Uri sliceUri) {
-        LiveData<Slice> sliceLiveData = mUriSliceMap.getOrDefault(sliceUri, null);
-        if (sliceLiveData == null) {
-            sliceLiveData = SliceLiveData.fromUri(mLauncher, sliceUri);
-            mUriSliceMap.put(sliceUri, sliceLiveData);
-        }
-        return sliceLiveData;
-    }
-
-    /**
-     * Start search session
-     */
-    public void start() {
-        stop();
-        mLogInstanceId = new InstanceIdSequence().newInstanceId();
-        mSearchWidgetHost = new SearchWidgetHost(mLauncher);
-        mSearchWidgetHost.startListening();
-    }
-
-    /**
      * Stop search session
      */
     public void stop() {
+        clearWidgetHost();
+    }
+
+    private void clearWidgetHost() {
         if (mSearchWidgetHost != null) {
             mSearchWidgetHost.stopListening();
+            mSearchWidgetHost.clearViews();
             mSearchWidgetHost.deleteHost();
-            for (SearchWidgetInfoContainer placeholder : mWidgetPlaceholders.values()) {
-                placeholder.clearListeners();
-            }
             mWidgetPlaceholders.clear();
             mSearchWidgetHost = null;
         }
-        for (LiveData<Slice> liveData : mUriSliceMap.values()) {
-            liveData.removeObservers(mLauncher);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) {
+        mPrevLauncherState = mLauncher.getStateManager().getCurrentStableState();
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (finalState != ALL_APPS) {
+            // Clear all search session related objects
+            mUriSliceMap.values().forEach(SliceLifeCycle::destroy);
+            mUriSliceMap.clear();
+
+            clearWidgetHost();
         }
-        mUriSliceMap.clear();
+
+        StatsLogger logger = mLauncher.getStatsLogManager().logger();
+        if (finalState.equals(ALL_APPS)) {
+            mLogInstanceId = new InstanceIdSequence().newInstanceId();
+            logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_ENTRY);
+        } else if (mPrevLauncherState.equals(ALL_APPS)) {
+            logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_EXIT);
+            mLogInstanceId = null;
+        }
+    }
+
+    /**
+     * Adds a new observer for the provided uri and returns a callback to cancel this observer
+     */
+    public SafeCloseable addObserver(Uri uri, Observer<Slice> listener) {
+        SliceLifeCycle slc = mUriSliceMap.get(uri);
+        if (slc == null) {
+            slc = new SliceLifeCycle(uri, mLauncher);
+            mUriSliceMap.put(uri, slc);
+        }
+        slc.addListener(listener);
+
+        final SliceLifeCycle sliceLifeCycle = slc;
+        return () -> sliceLifeCycle.removeListener(listener);
     }
 
     /**
@@ -159,5 +196,121 @@
                 AppWidgetProviderInfo appWidget) {
             return new SearchWidgetInfoContainer(context);
         }
+
+        @Override
+        public void clearViews() {
+            super.clearViews();
+        }
+    }
+
+    private static class SliceLifeCycle
+            implements ActivityLifecycleCallbacks, SliceCallback {
+
+        private final Uri mUri;
+        private final Launcher mLauncher;
+        private final SliceViewManager mSliceViewManager;
+        private final ArrayList<Observer<Slice>> mListeners = new ArrayList<>();
+
+        private boolean mDestroyed = false;
+        private boolean mWasListening = false;
+
+        SliceLifeCycle(Uri uri, Launcher launcher) {
+            mUri = uri;
+            mLauncher = launcher;
+            mSliceViewManager = SliceViewManager.getInstance(launcher);
+            launcher.registerActivityLifecycleCallbacks(this);
+
+            if (launcher.isDestroyed()) {
+                onActivityDestroyed(launcher);
+            } else if (launcher.isStarted()) {
+                onActivityStarted(launcher);
+            }
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+            destroy();
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+            updateListening();
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            updateListening();
+        }
+
+        private void updateListening() {
+            boolean isListening = mDestroyed
+                    ? false
+                    : (mLauncher.isStarted() && !mListeners.isEmpty());
+            UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening));
+        }
+
+        @WorkerThread
+        private void uploadListeningBg(boolean isListening) {
+            if (mWasListening != isListening) {
+                mWasListening = isListening;
+                if (isListening) {
+                    mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this);
+                    // Update slice one-time on the different thread so that we can display
+                    // multiple slices in parallel
+                    THREAD_POOL_EXECUTOR.execute(this::updateSlice);
+                } else {
+                    mSliceViewManager.unregisterSliceCallback(mUri, this);
+                }
+            }
+        }
+
+        @UiThread
+        private void addListener(Observer<Slice> listener) {
+            mListeners.add(listener);
+            updateListening();
+        }
+
+        @UiThread
+        private void removeListener(Observer<Slice> listener) {
+            mListeners.remove(listener);
+            updateListening();
+        }
+
+        @WorkerThread
+        private void updateSlice() {
+            try {
+                Slice s = mSliceViewManager.bindSlice(mUri);
+                MAIN_EXECUTOR.execute(() -> onSliceUpdated(s));
+            } catch (Exception e) {
+                Log.d(TAG, "Error fetching slice", e);
+            }
+        }
+
+        @UiThread
+        @Override
+        public void onSliceUpdated(@Nullable Slice s) {
+            mListeners.forEach(l -> l.onChanged(s));
+        }
+
+        private void destroy() {
+            if (mDestroyed) {
+                return;
+            }
+            mDestroyed = true;
+            mLauncher.unregisterActivityLifecycleCallbacks(this);
+            mListeners.clear();
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle bundle) { }
+
+        @Override
+        public void onActivityPaused(Activity activity) { }
+
+        @Override
+        public void onActivityResumed(Activity activity) { }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
     }
 }
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
index b5c2268..8e5f8cb 100644
--- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
+++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
@@ -70,10 +70,4 @@
         mListeners.remove(hostView);
     }
 
-    /**
-     * Removes all AppWidgetHost update listeners
-     */
-    public void clearListeners() {
-        mListeners.clear();
-    }
 }
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 8013557..61f2c2a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -31,7 +31,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
@@ -134,7 +133,7 @@
      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
      * @return a request ID that can be used to cancel the request.
      */
-    public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+    public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
             final ItemInfoWithIcon info) {
         Preconditions.assertUIThread();
         if (mPendingIconRequestCount <= 0) {
@@ -142,20 +141,18 @@
         }
         mPendingIconRequestCount ++;
 
-        IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
-            @Override
-            public void run() {
-                if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
-                    getTitleAndIcon(info, false);
-                } else if (info instanceof PackageItemInfo) {
-                    getTitleAndIconForApp((PackageItemInfo) info, false);
-                }
-                MAIN_EXECUTOR.execute(() -> {
-                    caller.reapplyItemInfo(info);
-                    onEnd();
-                });
-            }
-        };
+        HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
+                () -> {
+                    if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+                        getTitleAndIcon(info, false);
+                    } else if (info instanceof PackageItemInfo) {
+                        getTitleAndIconForApp((PackageItemInfo) info, false);
+                    }
+                    return info;
+                },
+                MAIN_EXECUTOR,
+                caller::reapplyItemInfo,
+                this::onIconRequestEnd);
         Utilities.postAsyncCallback(mWorkerHandler, request);
         return request;
     }
@@ -336,12 +333,6 @@
         return super.getEntryFromDB(cacheKey, entry, lowRes);
     }
 
-    public static abstract class IconLoadRequest extends HandlerRunnable {
-        IconLoadRequest(Handler handler, Runnable endRunnable) {
-            super(handler, endRunnable);
-        }
-    }
-
     /**
      * Interface for receiving itemInfo with high-res icon.
      */
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2066cd3..0292d20 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -344,6 +344,12 @@
 
         @UiEvent(doc = "Current grid size is changed to 2.")
         LAUNCHER_GRID_SIZE_2(665),
+
+        @UiEvent(doc = "Launcher entered into AllApps state.")
+        LAUNCHER_ALLAPPS_ENTRY(692),
+
+        @UiEvent(doc = "Launcher exited from AllApps state.")
+        LAUNCHER_ALLAPPS_EXIT(693),
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index d4fa278..e3e4b69 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -203,11 +203,16 @@
     /**
      * Add the icons for the supplied apk called packageName.
      */
-    public void addPackage(Context context, String packageName, UserHandle user) {
-        for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class)
-                .getActivityList(packageName, user)) {
+    public List<LauncherActivityInfo> addPackage(
+            Context context, String packageName, UserHandle user) {
+        List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
+                .getActivityList(packageName, user);
+
+        for (LauncherActivityInfo info : activities) {
             add(new AppInfo(context, info, user), info);
         }
+
+        return activities;
     }
 
     /**
@@ -250,7 +255,8 @@
     /**
      * Add and remove icons for this package which has been updated.
      */
-    public void updatePackage(Context context, String packageName, UserHandle user) {
+    public List<LauncherActivityInfo> updatePackage(
+            Context context, String packageName, UserHandle user) {
         final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
                 .getActivityList(packageName, user);
         if (matches.size() > 0) {
@@ -297,6 +303,8 @@
                 }
             }
         }
+
+        return matches;
     }
 
     /**
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3275d59..f13a109 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
@@ -51,6 +52,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
@@ -95,6 +97,7 @@
                 ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
                 : ItemInfoMatcher.ofPackages(packageSet, mUser);
         final HashSet<ComponentName> removedComponents = new HashSet<>();
+        final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
 
         switch (mOp) {
             case OP_ADD: {
@@ -104,7 +107,8 @@
                     if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
                         appsList.removePackage(packages[i], mUser);
                     }
-                    appsList.addPackage(context, packages[i], mUser);
+                    activitiesLists.put(
+                            packages[i], appsList.addPackage(context, packages[i], mUser));
                 }
                 flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
@@ -115,7 +119,8 @@
                     for (int i = 0; i < N; i++) {
                         if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
                         iconCache.updateIconsForPkg(packages[i], mUser);
-                        appsList.updatePackage(context, packages[i], mUser);
+                        activitiesLists.put(
+                                packages[i], appsList.updatePackage(context, packages[i], mUser));
                         app.getWidgetCache().removePackage(packages[i], mUser);
                     }
                 }
@@ -247,7 +252,14 @@
 
                         if (isNewApkAvailable
                                 && si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                            si.setProgressLevel(100, PackageInstallInfo.STATUS_INSTALLED);
+                            List<LauncherActivityInfo> activities = activitiesLists.get(
+                                    packageName);
+                            si.setProgressLevel(
+                                    activities == null || activities.isEmpty()
+                                            ? 100
+                                            : PackageManagerHelper.getLoadingProgress(
+                                                    activities.get(0)),
+                                    PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
                             iconCache.getTitleAndIcon(si, si.usingLowResIcon());
                             infoUpdated = true;
                         }
diff --git a/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..b90e43b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+    int VERSION = 1;
+
+    /** Register a listener to get Smartspace data. */
+    void registerListener(SmartspaceTargetListener listener);
+
+    /** Unregister a listener. */
+    void unregisterListener(SmartspaceTargetListener listener);
+
+    /** Provides Smartspace data to registered listeners. */
+    interface SmartspaceTargetListener {
+        /** Each Parcelable is a SmartspaceTarget that represents a card. */
+        void onSmartspaceTargetsUpdated(List<Parcelable> targets);
+    }
+}