Merge "Move FloatingTaskInit set up into init method"
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index cc83431..55e9887 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -56,11 +56,14 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.SettingsCache;
@@ -168,7 +171,7 @@
                 new TaskbarStashController(this),
                 new TaskbarEduController(this),
                 new TaskbarAutohideSuspendController(this),
-                new TaskbarPopupController());
+                new TaskbarPopupController(this));
     }
 
     public void init(TaskbarSharedState sharedState) {
@@ -316,6 +319,17 @@
         }
     }
 
+    @Override
+    public DotInfo getDotInfoForItem(ItemInfo info) {
+        return getPopupDataProvider().getDotInfoForItem(info);
+    }
+
+    @NonNull
+    @Override
+    public PopupDataProvider getPopupDataProvider() {
+        return mControllers.taskbarPopupController.getPopupDataProvider();
+    }
+
     /**
      * Sets a new data-source for this taskbar instance
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 56730db..a7c5d4d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -92,6 +92,7 @@
         stashedHandleViewController.init(this);
         taskbarStashController.init(this, sharedState);
         taskbarEduController.init(this);
+        taskbarPopupController.init(this);
     }
 
     /**
@@ -107,5 +108,6 @@
         taskbarViewController.onDestroy();
         stashedHandleViewController.onDestroy();
         taskbarAutohideSuspendController.onDestroy();
+        taskbarPopupController.onDestroy();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 952f597..06c75fc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -19,15 +19,25 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
+import com.android.launcher3.dot.FolderDotInfo;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.popup.PopupLiveUpdateHandler;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.HashMap;
 import java.util.Objects;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -39,11 +49,25 @@
     private static final SystemShortcut.Factory<TaskbarActivityContext>
             APP_INFO = SystemShortcut.AppInfo::new;
 
+    private final TaskbarActivityContext mContext;
     private final PopupDataProvider mPopupDataProvider;
 
-    public TaskbarPopupController() {
-        // TODO (b/198438631): add notifications dots change listener
-        mPopupDataProvider = new PopupDataProvider(packageUserKey -> {});
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    public TaskbarPopupController(TaskbarActivityContext context) {
+        mContext = context;
+        mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
+    }
+
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+
+        NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
+    }
+
+    public void onDestroy() {
+        NotificationListener.removeNotificationsChangedListener(mPopupDataProvider);
     }
 
     @NonNull
@@ -55,6 +79,38 @@
         mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
     }
 
+    private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
+        final PackageUserKey packageUserKey = new PackageUserKey(null, null);
+        Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
+                || updatedDots.test(packageUserKey);
+
+        LauncherBindableItemsContainer.ItemOperator op = (info, v) -> {
+            if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
+                if (matcher.test(info)) {
+                    ((BubbleTextView) v).applyDotState(info, true /* animate */);
+                }
+            } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
+                FolderInfo fi = (FolderInfo) info;
+                if (fi.contents.stream().anyMatch(matcher)) {
+                    FolderDotInfo folderDotInfo = new FolderDotInfo();
+                    for (WorkspaceItemInfo si : fi.contents) {
+                        folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si));
+                    }
+                    ((FolderIcon) v).setDotInfo(folderDotInfo);
+                }
+            }
+
+            // process all the shortcuts
+            return false;
+        };
+
+        mControllers.taskbarViewController.mapOverItems(op);
+        Folder folder = Folder.getOpen(mContext);
+        if (folder != null) {
+            folder.iterateOverItems(op);
+        }
+    }
+
     /**
      * Shows the notifications and deep shortcuts associated with a Taskbar {@param icon}.
      * @return the container if shown or null.
@@ -74,6 +130,8 @@
         final PopupContainerWithArrow<TaskbarActivityContext> container =
                 (PopupContainerWithArrow) context.getLayoutInflater().inflate(
                         R.layout.popup_container, context.getDragLayer(), false);
+        container.addOnAttachStateChangeListener(
+                new PopupLiveUpdateHandler<>(mContext, container));
         // TODO (b/198438631): configure for taskbar/context
 
         container.populateAndShow(icon,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 59393d7..c8d9fca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.views.ActivityContext;
 
 /**
@@ -330,4 +331,14 @@
         // Consider the overall visibility
         return getVisibility() == VISIBLE;
     }
+
+    protected void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+        // map over all the shortcuts on the taskbar
+        for (int i = 0; i < getChildCount(); i++) {
+            View item = getChildAt(i);
+            if (op.evaluate((ItemInfo) item.getTag(), item)) {
+                return;
+            }
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index c47bde9..96eac51 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
 
@@ -243,6 +244,10 @@
         mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
     }
 
+    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+        mTaskbarView.mapOverItems(op);
+    }
+
     /**
      * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
      * touch bounds.
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index ae3cc50..6addfe3 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -32,7 +32,6 @@
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
@@ -193,7 +192,6 @@
         boolean showAsGrid = dp.overviewShowAsGrid;
         boolean parallaxCenterAndAdjacentTask =
                 taskIndex != recentsView.getCurrentPage() && !showAsGrid;
-        float gridTranslationSecondary = recentsView.getGridTranslationSecondary(taskIndex);
         int startScroll = recentsView.getScrollOffset(taskIndex);
 
         RemoteTargetHandle[] topMostSimulators = null;
@@ -211,11 +209,9 @@
 
                 tvsLocal.fullScreenProgress.value = 0;
                 tvsLocal.recentsViewScale.value = 1;
-                if (showAsGrid) {
-                    tvsLocal.taskSecondaryTranslation.value = gridTranslationSecondary;
-                }
                 tvsLocal.setScroll(startScroll);
                 tvsLocal.setIsGridTask(v.isGridTask());
+                tvsLocal.setGridTranslationY(v.getGridTranslationY());
 
                 // Fade in the task during the initial 20% of the animation
                 out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1,
@@ -230,10 +226,6 @@
             out.setFloat(tvsLocal.recentsViewScale,
                     AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(),
                     TOUCH_RESPONSE_INTERPOLATOR);
-            if (showAsGrid) {
-                out.setFloat(tvsLocal.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
-                        TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL);
-            }
             out.setFloat(tvsLocal.recentsViewScroll, AnimatedFloat.VALUE, 0,
                     TOUCH_RESPONSE_INTERPOLATOR);
 
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 4f4edab..0ae415d 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.fallback;
 
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
+import static com.android.quickstep.ViewUtils.postFrameDrawn;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
 import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
@@ -219,8 +220,14 @@
 
     @Override
     public void onStateTransitionComplete(RecentsState finalState) {
-        setOverlayEnabled(finalState == DEFAULT || finalState == MODAL_TASK);
+        boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
+        setOverlayEnabled(isOverlayEnabled);
         setFreezeViewVisibility(false);
+
+        if (isOverlayEnabled) {
+            postFrameDrawn(this, () -> runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)));
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 146d235..7d396ba 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -102,6 +102,7 @@
     private StagedSplitBounds mStagedSplitBounds;
     private boolean mDrawsBelowRecents;
     private boolean mIsGridTask;
+    private float mGridTranslationY;
 
     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
         mContext = context;
@@ -156,9 +157,15 @@
             fullTaskSize = new Rect(mTaskRect);
             mOrientationState.getOrientationHandler()
                     .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
+            if (mIsGridTask) {
+                mTaskRect.offset(0, (int) mGridTranslationY);
+            }
         } else {
             fullTaskSize = mTaskRect;
         }
+        if (mIsGridTask) {
+            fullTaskSize.offset(0, (int) mGridTranslationY);
+        }
         return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
     }
 
@@ -218,6 +225,13 @@
     }
 
     /**
+     * Sets the y-translation when overview is in grid.
+     */
+    public void setGridTranslationY(float gridTranslationY) {
+        mGridTranslationY = gridTranslationY;
+    }
+
+    /**
      * Adds animation for all the components corresponding to transition from an app to overview.
      */
     public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
@@ -320,14 +334,12 @@
         mMatrix.postTranslate(insets.left, insets.top);
         mMatrix.postScale(scale, scale);
 
-        // Apply TaskView matrix: translate, scroll
+        // Apply TaskView matrix: taskRect, translate
         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
         mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
                 taskPrimaryTranslation.value);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskSecondaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(
-                mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
 
         // Apply RecentsView matrix
         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
@@ -335,6 +347,8 @@
                 recentsViewSecondaryTranslation.value);
         mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewPrimaryTranslation.value);
+        mOrientationState.getOrientationHandler().set(
+                mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
         applyWindowToHomeRotation(mMatrix);
 
         // Crop rect is the inverse of thumbnail matrix
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 5d6b656..3cba392 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.quickstep.ViewUtils.postFrameDrawn;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -104,8 +105,14 @@
 
     @Override
     public void onStateTransitionComplete(LauncherState finalState) {
-        setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
+        boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
+        setOverlayEnabled(isOverlayEnabled);
         setFreezeViewVisibility(false);
+
+        if (isOverlayEnabled) {
+            postFrameDrawn(this, () -> runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)));
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7efe879..7b5a6b9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1508,17 +1508,6 @@
             }
         }
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            // Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
-            // to reset the params after it settles in Overview from swipe up so that we don't
-            // render with obsolete param values.
-            runActionOnRemoteHandles(remoteTargetHandle -> {
-                TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
-                simulator.taskPrimaryTranslation.value = 0;
-                simulator.taskSecondaryTranslation.value = 0;
-                simulator.fullScreenProgress.value = 0;
-                simulator.recentsViewScale.value = 1;
-            });
-
             // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
             // null.
             if (!mRunningTaskShowScreenshot) {
@@ -1906,7 +1895,7 @@
         setEnableDrawingLiveTile(false);
         runActionOnRemoteHandles(remoteTargetHandle -> {
             remoteTargetHandle.getTransformParams().setTargetSet(null);
-            remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true);
+            remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
         });
         mSplitSelectStateController.resetState();
 
@@ -3016,6 +3005,10 @@
 
             @SuppressWarnings("WrongCall")
             private void onEnd(boolean success) {
+                // Reset task translations as they may have updated via animations in
+                // createTaskDismissAnimation
+                resetTaskVisuals();
+
                 if (success) {
                     if (shouldRemoveTask) {
                         if (dismissedTaskView.getTask() != null) {
@@ -3032,10 +3025,6 @@
                         }
                     }
 
-                    // Reset task translations as they may have updated via animations in
-                    // createTaskDismissAnimation
-                    resetTaskVisuals();
-
                     int pageToSnapTo = mCurrentPage;
                     mCurrentPageScrollDiff = 0;
                     int taskViewIdToSnapTo = -1;
@@ -4389,7 +4378,6 @@
             TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
             tvs.setOrientationState(mOrientationState);
             tvs.setDp(mActivity.getDeviceProfile());
-            tvs.setDrawsBelowRecents(true);
             tvs.recentsViewScale.value = 1;
         }
     }
@@ -4691,20 +4679,6 @@
         return position != -1 ? position : bottomRowIdArray.indexOf(taskView.getTaskViewId());
     }
 
-    /**
-     * Returns how many pixels the task is offset on the currently laid out secondary axis
-     * according to {@link #mGridProgress}.
-     */
-    public float getGridTranslationSecondary(int pageIndex) {
-        TaskView taskView = getTaskViewAt(pageIndex);
-        if (taskView == null) {
-            return 0;
-        }
-
-        return mOrientationHandler.getSecondaryValue(taskView.getGridTranslationX(),
-                taskView.getGridTranslationY());
-    }
-
     public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
         float degreesRotated;
         if (navbarRotation == 0) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 3da7893..e9a3779 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -648,21 +648,7 @@
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationStart(Animator animator) {
-                    recentsView.runActionOnRemoteHandles(
-                            (Consumer<RemoteTargetHandle>) remoteTargetHandle ->
-                                    remoteTargetHandle
-                                            .getTaskViewSimulator()
-                                            .setDrawsBelowRecents(false));
-                }
-
-                @Override
                 public void onAnimationEnd(Animator animator) {
-                    recentsView.runActionOnRemoteHandles(
-                            (Consumer<RemoteTargetHandle>) remoteTargetHandle ->
-                                    remoteTargetHandle
-                                            .getTaskViewSimulator()
-                                            .setDrawsBelowRecents(true));
                     mIsClickableAsLiveTile = true;
                 }
             });
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e8d6802..21b0edc 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -251,8 +251,7 @@
         // Tablet UI does not support emulated landscape.
         isTablet = allowRotation && info.isTablet(windowBounds);
         isPhone = !isTablet;
-        isTwoPanels = isTablet && useTwoPanels
-                && (isLandscape || FeatureFlags.ENABLE_TWO_PANEL_HOME_IN_PORTRAIT.get());
+        isTwoPanels = isTablet && useTwoPanels;
 
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8154168..0656125 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -669,6 +669,8 @@
         return !isWorkspaceLoading();
     }
 
+    @NonNull
+    @Override
     public PopupDataProvider getPopupDataProvider() {
         return mPopupDataProvider;
     }
@@ -948,7 +950,7 @@
         hideKeyboard();
         logStopAndResume(false /* isResume */);
         mAppWidgetHost.setActivityStarted(false);
-        NotificationListener.removeNotificationsChangedListener();
+        NotificationListener.removeNotificationsChangedListener(getPopupDataProvider());
     }
 
     @Override
@@ -977,7 +979,7 @@
         mModel.validateModelDataOnResume();
 
         // Set the notification listener and fetch updated notifications when we resume
-        NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+        NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
 
         DiscoveryBounce.showForHomeIfNeeded(this);
         mAppWidgetHost.setActivityResumed(true);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index e253505..c4d8166 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -210,10 +210,6 @@
             "ENABLE_TWO_PANEL_HOME", true,
             "Uses two panel on home screen. Only applicable on large screen devices.");
 
-    public static final BooleanFlag ENABLE_TWO_PANEL_HOME_IN_PORTRAIT = getDebugFlag(
-            "ENABLE_TWO_PANEL_HOME_IN_PORTRAIT", true,
-            "Uses two panel on home screen in portrait if ENABLE_TWO_PANEL_HOME is enabled.");
-
     public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
             "ENABLE_SCRIM_FOR_APP_LAUNCH", false,
             "Enables scrim during app launch animation.");
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index d27d8c7..ebe45a5 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -29,12 +29,13 @@
 import android.view.View;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * An object that contains relevant information from a {@link StatusBarNotification}. This should
@@ -99,21 +100,23 @@
         if (intent == null) {
             return;
         }
-        final Launcher launcher = Launcher.getLauncher(view.getContext());
+        final ActivityContext context = ActivityContext.lookupContext(view.getContext());
         Bundle activityOptions = ActivityOptions.makeClipRevealAnimation(
                 view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
         try {
             intent.send(null, 0, null, null, null, null, activityOptions);
-            launcher.getStatsLogManager().logger().withItemInfo(mItemInfo)
+            context.getStatsLogManager().logger().withItemInfo(mItemInfo)
                     .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
         } catch (PendingIntent.CanceledException e) {
             e.printStackTrace();
         }
         if (autoCancel) {
-            launcher.getPopupDataProvider().cancelNotification(notificationKey);
+            PopupDataProvider popupDataProvider = context.getPopupDataProvider();
+            if (popupDataProvider != null) {
+                popupDataProvider.cancelNotification(notificationKey);
+            }
         }
-        AbstractFloatingView.closeOpenContainer(launcher, AbstractFloatingView
-                .TYPE_ACTION_POPUP);
+        AbstractFloatingView.closeOpenContainer(context, AbstractFloatingView.TYPE_ACTION_POPUP);
     }
 
     public Drawable getIconForBackground(Context context, int background) {
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index e58f5fa..bbeb886 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -30,6 +30,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 
@@ -66,7 +67,8 @@
     private static final int MSG_RANKING_UPDATE = 5;
 
     private static NotificationListener sNotificationListenerInstance = null;
-    private static NotificationsChangedListener sNotificationsChangedListener;
+    private static final ArraySet<NotificationsChangedListener> sNotificationsChangedListeners =
+            new ArraySet<>();
     private static boolean sIsConnected;
 
     private final Handler mWorkerHandler;
@@ -94,8 +96,11 @@
         return sIsConnected ? sNotificationListenerInstance : null;
     }
 
-    public static void setNotificationsChangedListener(NotificationsChangedListener listener) {
-        sNotificationsChangedListener = listener;
+    public static void addNotificationsChangedListener(NotificationsChangedListener listener) {
+        if (listener == null) {
+            return;
+        }
+        sNotificationsChangedListeners.add(listener);
 
         NotificationListener notificationListener = getInstanceIfConnected();
         if (notificationListener != null) {
@@ -108,8 +113,10 @@
         }
     }
 
-    public static void removeNotificationsChangedListener() {
-        sNotificationsChangedListener = null;
+    public static void removeNotificationsChangedListener(NotificationsChangedListener listener) {
+        if (listener != null) {
+            sNotificationsChangedListeners.remove(listener);
+        }
     }
 
     private boolean handleWorkerMessage(Message message) {
@@ -180,23 +187,27 @@
     private boolean handleUiMessage(Message message) {
         switch (message.what) {
             case MSG_NOTIFICATION_POSTED:
-                if (sNotificationsChangedListener != null) {
+                if (sNotificationsChangedListeners.size() > 0) {
                     Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
-                    sNotificationsChangedListener.onNotificationPosted(
-                            msg.first, msg.second);
+                    for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
+                        listener.onNotificationPosted(msg.first, msg.second);
+                    }
                 }
                 break;
             case MSG_NOTIFICATION_REMOVED:
-                if (sNotificationsChangedListener != null) {
+                if (sNotificationsChangedListeners.size() > 0) {
                     Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
-                    sNotificationsChangedListener.onNotificationRemoved(
-                            msg.first, msg.second);
+                    for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
+                        listener.onNotificationRemoved(msg.first, msg.second);
+                    }
                 }
                 break;
             case MSG_NOTIFICATION_FULL_REFRESH:
-                if (sNotificationsChangedListener != null) {
-                    sNotificationsChangedListener.onNotificationFullRefresh(
-                            (List<StatusBarNotification>) message.obj);
+                if (sNotificationsChangedListeners.size() > 0) {
+                    for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
+                        listener.onNotificationFullRefresh(
+                                (List<StatusBarNotification>) message.obj);
+                    }
                 }
                 break;
         }
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index f9ff8a6..16a4057 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -38,11 +38,12 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a single notification,
@@ -320,9 +321,12 @@
     }
 
     public void onChildDismissed() {
-        Launcher launcher = Launcher.getLauncher(getContext());
-        launcher.getPopupDataProvider().cancelNotification(
-                mNotificationInfo.notificationKey);
-        launcher.getStatsLogManager().logger().log(LAUNCHER_NOTIFICATION_DISMISSED);
+        ActivityContext activityContext = ActivityContext.lookupContext(getContext());
+        PopupDataProvider popupDataProvider = activityContext.getPopupDataProvider();
+        if (popupDataProvider == null) {
+            return;
+        }
+        popupDataProvider.cancelNotification(mNotificationInfo.notificationKey);
+        activityContext.getStatsLogManager().logger().log(LAUNCHER_NOTIFICATION_DISMISSED);
     }
 }
diff --git a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
new file mode 100644
index 0000000..731f439
--- /dev/null
+++ b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
@@ -0,0 +1,89 @@
+/*
+ * 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.launcher3.popup;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Utility class to handle updates while the popup is visible on the Launcher
+ */
+public class LauncherPopupLiveUpdateHandler extends PopupLiveUpdateHandler<Launcher> {
+
+    public LauncherPopupLiveUpdateHandler(
+            Launcher launcher, PopupContainerWithArrow<Launcher> popupContainerWithArrow) {
+        super(launcher, popupContainerWithArrow);
+    }
+
+    private View getWidgetsView(ViewGroup container) {
+        for (int i = container.getChildCount() - 1; i >= 0; --i) {
+            View systemShortcutView = container.getChildAt(i);
+            if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
+                return systemShortcutView;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void onWidgetsBound() {
+        ItemInfo itemInfo = (ItemInfo) mPopupContainerWithArrow.getOriginalIcon().getTag();
+        SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mContext, itemInfo);
+        View widgetsView = getWidgetsView(mPopupContainerWithArrow);
+        if (widgetsView == null && mPopupContainerWithArrow.getWidgetContainer() != null) {
+            widgetsView = getWidgetsView(mPopupContainerWithArrow.getWidgetContainer());
+        }
+
+        if (widgetInfo != null && widgetsView == null) {
+            // We didn't have any widgets cached but now there are some, so enable the shortcut.
+            if (mPopupContainerWithArrow.getSystemShortcutContainer()
+                    != mPopupContainerWithArrow) {
+                if (mPopupContainerWithArrow.getWidgetContainer() == null) {
+                    mPopupContainerWithArrow.setWidgetContainer(
+                            mPopupContainerWithArrow.inflateAndAdd(
+                                    R.layout.widget_shortcut_container,
+                                    mPopupContainerWithArrow));
+                }
+                mPopupContainerWithArrow.initializeSystemShortcut(
+                        R.layout.system_shortcut,
+                        mPopupContainerWithArrow.getWidgetContainer(),
+                        widgetInfo);
+            } else {
+                // If using the expanded system shortcut (as opposed to just the icon), we need
+                // to reopen the container to ensure measurements etc. all work out. While this
+                // could be quite janky, in practice the user would typically see a small
+                // flicker as the animation restarts partway through, and this is a very rare
+                // edge case anyway.
+                mPopupContainerWithArrow.close(false);
+                PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon());
+            }
+        } else if (widgetInfo == null && widgetsView != null) {
+            // No widgets exist, but we previously added the shortcut so remove it.
+            if (mPopupContainerWithArrow.getSystemShortcutContainer()
+                    != mPopupContainerWithArrow
+                    && mPopupContainerWithArrow.getWidgetContainer() != null) {
+                mPopupContainerWithArrow.getWidgetContainer().removeView(widgetsView);
+            } else {
+                mPopupContainerWithArrow.close(false);
+                PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon());
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 278f57d..aa8c70b 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -60,7 +60,6 @@
 import com.android.launcher3.notification.NotificationContainer;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
-import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
 import com.android.launcher3.touch.ItemLongClickListener;
@@ -72,9 +71,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -225,7 +222,8 @@
     }
 
     private void configureForLauncher(Launcher launcher) {
-        addOnAttachStateChangeListener(new LiveUpdateHandler(launcher));
+        addOnAttachStateChangeListener(new LauncherPopupLiveUpdateHandler(
+                launcher, (PopupContainerWithArrow<Launcher>) this));
         mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
         mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(launcher);
         launcher.getDragController().addDragListener(this);
@@ -329,6 +327,26 @@
                 this, mShortcuts, notificationKeys));
     }
 
+    protected NotificationContainer getNotificationContainer() {
+        return mNotificationContainer;
+    }
+
+    protected BubbleTextView getOriginalIcon() {
+        return mOriginalIcon;
+    }
+
+    protected ViewGroup getSystemShortcutContainer() {
+        return mSystemShortcutContainer;
+    }
+
+    protected ViewGroup getWidgetContainer() {
+        return mWidgetContainer;
+    }
+
+    protected void setWidgetContainer(ViewGroup widgetContainer) {
+        mWidgetContainer = widgetContainer;
+    }
+
     private String getTitleForAccessibility() {
         return getContext().getString(mNumNotifications == 0 ?
                 R.string.action_deep_shortcut :
@@ -352,7 +370,7 @@
         }
     }
 
-    private void updateHiddenShortcuts() {
+    protected void updateHiddenShortcuts() {
         int allowedCount = mNotificationContainer != null
                 ? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
 
@@ -363,7 +381,7 @@
         }
     }
 
-    private void initializeSystemShortcut(int resId, ViewGroup container, SystemShortcut info) {
+    protected void initializeSystemShortcut(int resId, ViewGroup container, SystemShortcut info) {
         View view = inflateAndAdd(
                 resId, container, getInsertIndexForSystemShortcut(container, info));
         if (view instanceof DeepShortcutView) {
@@ -436,7 +454,7 @@
         };
     }
 
-    private void updateNotificationHeader() {
+    protected void updateNotificationHeader() {
         ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
         DotInfo dotInfo = mActivityContext.getDotInfoForItem(itemInfo);
         if (mNotificationContainer != null && dotInfo != null) {
@@ -495,118 +513,6 @@
     }
 
     /**
-     * Utility class to handle updates while the popup is visible (like widgets and
-     * notification changes)
-     */
-    private class LiveUpdateHandler implements
-            PopupDataChangeListener, View.OnAttachStateChangeListener {
-
-        private final Launcher mLauncher;
-
-        LiveUpdateHandler(Launcher launcher) {
-            mLauncher = launcher;
-        }
-
-        @Override
-        public void onViewAttachedToWindow(View view) {
-            mLauncher.getPopupDataProvider().setChangeListener(this);
-        }
-
-        @Override
-        public void onViewDetachedFromWindow(View view) {
-            mLauncher.getPopupDataProvider().setChangeListener(null);
-        }
-
-        private View getWidgetsView(ViewGroup container) {
-            for (int i = container.getChildCount() - 1; i >= 0; --i) {
-                View systemShortcutView = container.getChildAt(i);
-                if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
-                    return systemShortcutView;
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public void onWidgetsBound() {
-            ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
-            SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
-            View widgetsView = getWidgetsView(PopupContainerWithArrow.this);
-            if (widgetsView == null && mWidgetContainer != null) {
-                widgetsView = getWidgetsView(mWidgetContainer);
-            }
-
-            if (widgetInfo != null && widgetsView == null) {
-                // We didn't have any widgets cached but now there are some, so enable the shortcut.
-                if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
-                    if (mWidgetContainer == null) {
-                        mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
-                                PopupContainerWithArrow.this);
-                    }
-                    initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
-                            widgetInfo);
-                } else {
-                    // If using the expanded system shortcut (as opposed to just the icon), we need
-                    // to reopen the container to ensure measurements etc. all work out. While this
-                    // could be quite janky, in practice the user would typically see a small
-                    // flicker as the animation restarts partway through, and this is a very rare
-                    // edge case anyway.
-                    close(false);
-                    PopupContainerWithArrow.showForIcon(mOriginalIcon);
-                }
-            } else if (widgetInfo == null && widgetsView != null) {
-                // No widgets exist, but we previously added the shortcut so remove it.
-                if (mSystemShortcutContainer
-                        != PopupContainerWithArrow.this
-                        && mWidgetContainer != null) {
-                    mWidgetContainer.removeView(widgetsView);
-                } else {
-                    close(false);
-                    PopupContainerWithArrow.showForIcon(mOriginalIcon);
-                }
-            }
-        }
-
-        /**
-         * Updates the notification header if the original icon's dot updated.
-         */
-        @Override
-        public void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) {
-            ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
-            PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
-            if (updatedDots.test(packageUser)) {
-                updateNotificationHeader();
-            }
-        }
-
-
-        @Override
-        public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
-            if (mNotificationContainer == null) {
-                return;
-            }
-            ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
-            DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
-            if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
-                // No more notifications, remove the notification views and expand all shortcuts.
-                mNotificationContainer.setVisibility(GONE);
-                updateHiddenShortcuts();
-                assignMarginsAndBackgrounds(PopupContainerWithArrow.this);
-                updateArrowColor();
-            } else {
-                mNotificationContainer.trimNotifications(
-                        NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
-            }
-        }
-
-        @Override
-        public void onSystemShortcutsUpdated() {
-            close(true);
-            PopupContainerWithArrow.showForIcon(mOriginalIcon);
-        }
-    }
-
-    /**
      * Dismisses the popup if it is no longer valid
      */
     public static void dismissInvalidPopup(BaseDraggingActivity activity) {
diff --git a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
new file mode 100644
index 0000000..194c22f
--- /dev/null
+++ b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
@@ -0,0 +1,108 @@
+/*
+ * 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.launcher3.popup;
+
+import static android.view.View.GONE;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.notification.NotificationContainer;
+import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.views.ActivityContext;
+
+import java.util.Map;
+import java.util.function.Predicate;
+
+/**
+ * Utility class to handle updates while the popup is visible (like widgets and
+ * notification changes)
+ *
+ * @param <T> The activity on which the popup shows
+ */
+public class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
+        PopupDataProvider.PopupDataChangeListener, View.OnAttachStateChangeListener {
+
+    protected final T mContext;
+    protected final PopupContainerWithArrow<T> mPopupContainerWithArrow;
+
+    public PopupLiveUpdateHandler(
+            T context, PopupContainerWithArrow<T> popupContainerWithArrow) {
+        mContext = context;
+        mPopupContainerWithArrow = popupContainerWithArrow;
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View view) {
+        PopupDataProvider popupDataProvider = mContext.getPopupDataProvider();
+
+        if (popupDataProvider != null) {
+            popupDataProvider.setChangeListener(this);
+        }
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View view) {
+        PopupDataProvider popupDataProvider = mContext.getPopupDataProvider();
+
+        if (popupDataProvider != null) {
+            popupDataProvider.setChangeListener(null);
+        }
+    }
+
+    /**
+     * Updates the notification header if the original icon's dot updated.
+     */
+    @Override
+    public void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) {
+        ItemInfo itemInfo = (ItemInfo) mPopupContainerWithArrow.getOriginalIcon().getTag();
+        PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
+        if (updatedDots.test(packageUser)) {
+            mPopupContainerWithArrow.updateNotificationHeader();
+        }
+    }
+
+
+    @Override
+    public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
+        NotificationContainer notificationContainer =
+                mPopupContainerWithArrow.getNotificationContainer();
+        if (notificationContainer == null) {
+            return;
+        }
+        ItemInfo originalInfo = (ItemInfo) mPopupContainerWithArrow.getOriginalIcon().getTag();
+        DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
+        if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
+            // No more notifications, remove the notification views and expand all shortcuts.
+            notificationContainer.setVisibility(GONE);
+            mPopupContainerWithArrow.updateHiddenShortcuts();
+            mPopupContainerWithArrow.assignMarginsAndBackgrounds(mPopupContainerWithArrow);
+            mPopupContainerWithArrow.updateArrowColor();
+        } else {
+            notificationContainer.trimNotifications(
+                    NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
+        }
+    }
+
+    @Override
+    public void onSystemShortcutsUpdated() {
+        mPopupContainerWithArrow.close(true);
+        PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon());
+    }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 1820933..0d8602f 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
@@ -177,12 +178,16 @@
         if (!ShortcutUtil.supportsShortcuts(item)) {
             return false;
         }
+        PopupDataProvider popupDataProvider = mActivity.getPopupDataProvider();
+        if (popupDataProvider == null) {
+            return false;
+        }
         final PopupContainerWithArrow container =
                 (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
                         R.layout.popup_container, mActivity.getDragLayer(), false);
 
         container.populateAndShow((BubbleTextView) v,
-                mActivity.getPopupDataProvider().getShortcutCountForItem(item),
+                popupDataProvider.getShortcutCountForItem(item),
                 Collections.emptyList(),
                 Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item),
                         APP_INFO.getShortcut(mActivity, item)));
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 8caf886..ba9d09c 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -451,24 +451,19 @@
     public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
             StagedSplitBounds splitInfo, int desiredStagePosition) {
         boolean isLandscape = dp.isLandscape;
-        float verticalDividerDiff = splitInfo.visualDividerBounds.height() / 2f;
-        float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f;
-        float diff;
         if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
             if (isLandscape) {
-                diff = outRect.width() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff;
-                outRect.right -= diff;
+                outRect.right = outRect.left + (int) (outRect.width() * splitInfo.leftTaskPercent);
             } else {
-                diff = outRect.height() * (1f - splitInfo.topTaskPercent) + verticalDividerDiff;
-                outRect.bottom -= diff;
+                outRect.bottom = outRect.top + (int) (outRect.height() * splitInfo.topTaskPercent);
             }
         } else {
             if (isLandscape) {
-                diff = outRect.width() * splitInfo.leftTaskPercent + horizontalDividerDiff;
-                outRect.left += diff;
+                outRect.left += (int) (outRect.width() *
+                        (splitInfo.leftTaskPercent + splitInfo.dividerWidthPercent));
             } else {
-                diff = outRect.height() * splitInfo.topTaskPercent + verticalDividerDiff;
-                outRect.top += diff;
+                outRect.top += (int) (outRect.height() *
+                        (splitInfo.topTaskPercent + splitInfo.dividerHeightPercent));
             }
         }
     }
@@ -479,9 +474,9 @@
             StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
         int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
         int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
-        int dividerBar = (splitBoundsConfig.appsStackedVertically ?
-                splitBoundsConfig.visualDividerBounds.height() :
-                splitBoundsConfig.visualDividerBounds.width());
+        int dividerBar = splitBoundsConfig.appsStackedVertically
+                ? (int) (splitBoundsConfig.dividerHeightPercent * parentHeight)
+                : (int) (splitBoundsConfig.dividerWidthPercent * parentWidth);
         int primarySnapshotHeight;
         int primarySnapshotWidth;
         int secondarySnapshotHeight;
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 6aef38f..53b1c3e 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -99,6 +99,8 @@
         // This class is orientation-agnostic, so we compute both for later use
         public final float topTaskPercent;
         public final float leftTaskPercent;
+        public final float dividerWidthPercent;
+        public final float dividerHeightPercent;
         /**
          * If {@code true}, that means at the time of creation of this object, the
          * split-screened apps were vertically stacked. This is useful in scenarios like
@@ -130,6 +132,8 @@
 
             leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
             topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
+            dividerWidthPercent = visualDividerBounds.width() / (float) rightBottomBounds.right;
+            dividerHeightPercent = visualDividerBounds.height() / (float) rightBottomBounds.bottom;
         }
     }
 
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index a2e4ad6..e09eff6 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.ViewCache;
 
 /**
@@ -166,4 +167,9 @@
             // No op.
         };
     }
+
+    @Nullable
+    default PopupDataProvider getPopupDataProvider() {
+        return null;
+    }
 }