Merge changes I838ef4de,I22e2948a into ub-launcher3-master

* changes:
  Use onQuickStep call from SysUI
  Workaround for ensuring dummy task view has valid task
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 5f94bca..9f7fef3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -16,10 +16,8 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_X_FACTOR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
@@ -51,7 +49,6 @@
     @Override
     public void setState(LauncherState state) {
         mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
-        updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher));
         float[] translationFactor = state.getOverviewTranslationFactor(mLauncher);
         mRecentsView.setTranslationXFactor(translationFactor[0]);
         mRecentsView.setTranslationYFactor(translationFactor[1]);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 5d975b0..e6d06da 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.Utilities.getPrefs;
 import static com.android.quickstep.OverviewInteractionState.KEY_SWIPE_UP_ENABLED;
+import static com.android.launcher3.LauncherState.ALL_APPS;
 
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -29,6 +30,7 @@
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 
 public class UiFactory {
 
@@ -85,6 +87,14 @@
         }
     }
 
+    public static void onLauncherStateOrResumeChanged(Launcher launcher) {
+        WindowManagerWrapper.getInstance().setShelfHeight(
+                launcher.getStateManager().getState() != ALL_APPS &&
+                        launcher.isUserActive() &&
+                        !launcher.getDeviceProfile().isVerticalBarLayout(),
+                launcher.getDeviceProfile().hotseatBarSizePx);
+    }
+
     public static void onTrimMemory(Context context, int level) {
         RecentsModel model = RecentsModel.getInstance(context);
         if (model != null) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 34c3e4f..55ed9a9 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -25,6 +25,7 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.View;
+import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.DeviceProfile;
@@ -66,8 +67,11 @@
                 }
             };
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private float mTranslationXFactor;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private float mTranslationYFactor;
+
     private Rect mPagePadding = new Rect();
 
     public LauncherRecentsView(Context context) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index bc4cd90..7a5fd2f 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -47,6 +47,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewDebug;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
@@ -136,6 +137,7 @@
 
     private PendingAnimation mPendingAnimation;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private float mContentAlpha = 1;
 
     // Keeps track of task views whose visual state should not be reset
@@ -691,6 +693,9 @@
     }
 
     private void snapToPageRelative(int delta) {
+        if (getPageCount() == 0) {
+            return;
+        }
         snapToPage((getNextPage() + getPageCount() + delta) % getPageCount());
     }
 
@@ -758,6 +763,7 @@
         if (mContentAlpha == alpha) {
             return;
         }
+
         mContentAlpha = alpha;
         for (int i = getChildCount() - 1; i >= 0; i--) {
             getChildAt(i).setAlpha(alpha);
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index e443eea..4c8d69f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -139,6 +139,7 @@
     }
 
     private void updateThumbnailMatrix() {
+        boolean rotate = false;
         if (mBitmapShader != null && mThumbnailData != null) {
             float scale = mThumbnailData.scale;
             float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
@@ -146,8 +147,6 @@
             float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
                     (mThumbnailData.insets.top + mThumbnailData.insets.bottom) * scale;
             final float thumbnailScale;
-
-            boolean rotate = false;
             final DeviceProfile profile = BaseActivity.fromContext(getContext())
                     .getDeviceProfile();
             if (getMeasuredWidth() == 0) {
@@ -226,7 +225,12 @@
             mPaint.setShader(shader);
         }
 
-        mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
+        if (rotate) {
+            // The overlay doesn't really work when the screenshot is rotated, so don't add it.
+            mOverlay.reset();
+        } else {
+            mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
+        }
         invalidate();
     }
 
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
index d858d3e..5537bc6 100644
--- a/res/layout/all_apps_fast_scroller.xml
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -21,8 +21,8 @@
         android:id="@+id/fast_scroller_popup"
         style="@style/FastScrollerPopup"
         android:layout_alignParentEnd="true"
-        android:layout_marginEnd="@dimen/fastscroll_popup_margin"
-        android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin" />
+        android:layout_below="@+id/search_container_all_apps"
+        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
 
     <com.android.launcher3.views.RecyclerViewFastScroller
         android:id="@+id/fast_scroller"
@@ -30,8 +30,8 @@
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
         android:layout_alignParentEnd="true"
+        android:layout_below="@+id/search_container_all_apps"
         android:layout_marginEnd="@dimen/fastscroll_end_margin"
-        android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin"
         launcher:canThumbDetach="true" />
 
 </merge>
\ No newline at end of file
diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml
index f88c600..c4240f8 100644
--- a/res/layout/all_apps_floating_header.xml
+++ b/res/layout/all_apps_floating_header.xml
@@ -18,10 +18,10 @@
     android:id="@+id/all_apps_header"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin"
+    android:layout_below="@id/search_container_all_apps"
     android:clipToPadding="false"
-    android:orientation="vertical"
-    android:paddingTop="@dimen/all_apps_header_top_padding" >
+    android:paddingTop="@dimen/all_apps_header_top_padding"
+    android:orientation="vertical" >
 
     <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
         android:id="@+id/tabs"
diff --git a/res/layout/all_apps_rv_layout.xml b/res/layout/all_apps_rv_layout.xml
index 8eba7fe..c353b36 100644
--- a/res/layout/all_apps_rv_layout.xml
+++ b/res/layout/all_apps_rv_layout.xml
@@ -19,7 +19,7 @@
     android:id="@+id/apps_list_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin"
+    android:layout_below="@id/search_container_all_apps"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
     android:focusable="true" />
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index fea2eea..2accd2d 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -20,8 +20,9 @@
     android:id="@+id/all_apps_tabs_view_pager"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_below="@id/search_container_all_apps"
     android:layout_gravity="center_horizontal|top"
-    android:layout_marginTop="@dimen/all_apps_tabs_top_margin"
+    android:layout_marginTop="@dimen/all_apps_header_tab_height"
     android:clipChildren="true"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 6cf23ad..7b52529 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -31,9 +31,6 @@
     <dimen name="dynamic_grid_cell_layout_padding">0dp</dimen>
     <dimen name="dynamic_grid_cell_layout_bottom_padding">5.5dp</dimen>
 
-    <!-- Folders -->
-    <dimen name="folder_preview_padding">2dp</dimen>
-
     <!-- Hotseat -->
     <!-- Will be set to equal the hotseat icon size. -->
     <dimen name="dynamic_grid_hotseat_size">0dp</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b7e7ca1..a2f7286 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -95,12 +95,7 @@
 
     <dimen name="all_apps_divider_margin_vertical">8dp</dimen>
 
-    <!-- Derived dimens -->
-    <dimen name="all_apps_search_bar_field_height_and_margin">56dp</dimen>
-    <!-- all_apps_search_bar_field_height_and_margin + all_apps_header_tab_height -->
-    <dimen name="all_apps_tabs_top_margin">106dp</dimen>
-
- <!-- Widget tray -->
+<!-- Widget tray -->
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">16dp</dimen>
 
@@ -142,8 +137,6 @@
     <dimen name="spring_loaded_panel_border">1dp</dimen>
 
 <!-- Folders -->
-    <!-- The size of the padding on the preview background drawable -->
-    <dimen name="folder_preview_padding">10dp</dimen>
     <dimen name="page_indicator_dot_size">8dp</dimen>
 
     <dimen name="folder_cell_x_padding">9dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7d5d81c..bcb90e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -74,7 +74,7 @@
     <!-- Drag and drop -->
     <!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
     <string name="long_press_shortcut_to_add">Touch &amp; hold to pick up a shortcut.</string>
-    <!-- Accessibility spoken hint message in deep shortcut menu, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
+    <!-- Accessibility spoken hint message in deep shortcut menu, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=200] -->
     <string name="long_accessible_way_to_add_shortcut">Double-tap &amp; hold to pick up a shortcut or use custom actions.</string>
 
     <skip />
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 02d70c4..cf2d79f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -39,6 +39,7 @@
     protected SystemUiController mSystemUiController;
 
     private boolean mStarted;
+    private boolean mUserActive;
 
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
@@ -85,6 +86,18 @@
     }
 
     @Override
+    protected void onResume() {
+        mUserActive = true;
+        super.onResume();
+    }
+
+    @Override
+    protected void onUserLeaveHint() {
+        mUserActive = false;
+        super.onUserLeaveHint();
+    }
+
+    @Override
     protected void onStop() {
         mStarted = false;
         super.onStop();
@@ -94,6 +107,10 @@
         return mStarted;
     }
 
+    public boolean isUserActive() {
+        return mUserActive;
+    }
+
     public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
         mDPChangeListeners.add(listener);
     }
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 458f7b2..34819af 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -45,7 +45,7 @@
     private static final String TAG = "BaseDraggingActivity";
 
     // The Intent extra that defines whether to ignore the launch animation
-    private static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
+    protected static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
 
     // When starting an action mode, setting this tag will cause the action mode to be cancelled
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 14d8b93..6f35752 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -28,6 +28,7 @@
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.graphics.IconNormalizer;
 
 public class DeviceProfile {
 
@@ -81,9 +82,8 @@
     public int workspaceCellPaddingXPx;
 
     // Folder
-    public int folderBackgroundOffset;
     public int folderIconSizePx;
-    public int folderIconPreviewPadding;
+    public int folderIconOffsetYPx;
 
     // Folder cell
     public int folderCellWidthPx;
@@ -340,9 +340,8 @@
         }
 
         // Folder icon
-        folderBackgroundOffset = -iconDrawablePaddingPx;
-        folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
-        folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
+        folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
+        folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
     }
 
     private void updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res) {
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index dec6cb4..d025a9b 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
 import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 
 import android.animation.TimeInterpolator;
 import android.content.Context;
@@ -47,7 +46,7 @@
     protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
 
     private final Runnable mFadeAnimationEndRunnable =
-            () -> updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
+            () -> updateVisibility(DropTargetBar.this);
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected boolean mDeferOnDragEnd;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 43a781d..11e8c57 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 
 import android.animation.Animator;
@@ -147,7 +146,6 @@
     private static final int REQUEST_CREATE_APPWIDGET = 5;
 
     private static final int REQUEST_PICK_APPWIDGET = 9;
-    private static final int REQUEST_PICK_WALLPAPER = 10;
 
     private static final int REQUEST_BIND_APPWIDGET = 11;
     public static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
@@ -381,6 +379,7 @@
         }
 
         mOldConfig.setTo(newConfig);
+        UiFactory.onLauncherStateOrResumeChanged(this);
         super.onConfigurationChanged(newConfig);
     }
 
@@ -573,14 +572,6 @@
                         ON_ACTIVITY_RESULT_ANIMATION_DELAY);
             }
             return;
-        } else if (requestCode == REQUEST_PICK_WALLPAPER) {
-            if (resultCode == RESULT_OK && isInState(OVERVIEW)) {
-                // User could have free-scrolled between pages before picking a wallpaper; make sure
-                // we move to the closest one now.
-                mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
-                mStateManager.goToState(NORMAL, false);
-            }
-            return;
         }
 
         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
@@ -822,6 +813,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
+        UiFactory.onLauncherStateOrResumeChanged(this);
 
         TraceHelper.endSection("ON_RESUME");
     }
@@ -841,6 +833,12 @@
     }
 
     @Override
+    protected void onUserLeaveHint() {
+        super.onUserLeaveHint();
+        UiFactory.onLauncherStateOrResumeChanged(this);
+    }
+
+    @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
         mStateManager.onWindowFocusChanged();
@@ -1663,35 +1661,19 @@
             Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
             return;
         }
-
         int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
         float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
-        setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
                 .putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
 
         String pickerPackage = getString(R.string.wallpaper_picker_package);
-        boolean hasTargetPackage = !TextUtils.isEmpty(pickerPackage);
-        if (hasTargetPackage) {
+        if (!TextUtils.isEmpty(pickerPackage)) {
             intent.setPackage(pickerPackage);
-        }
-
-        final Bundle launchOptions;
-        if (v != null) {
-            intent.setSourceBounds(getViewBounds(v));
-            // If there is no target package, use the default intent chooser animation
-            launchOptions = hasTargetPackage
-                    ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
-                    : null;
         } else {
-            launchOptions = null;
+            // If there is no target package, use the default intent chooser animation
+            intent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
         }
-        try {
-            startActivityForResult(intent, REQUEST_PICK_WALLPAPER, launchOptions);
-        } catch (ActivityNotFoundException e) {
-            setWaitingForResult(null);
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-        }
+        startActivitySafely(v, intent, null);
     }
 
     @TargetApi(Build.VERSION_CODES.M)
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 7d50a52..ef285df 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -294,6 +294,7 @@
             // Only disable clipping if needed, otherwise leave it as previous value.
             mLauncher.getWorkspace().setClipChildren(false);
         }
+        UiFactory.onLauncherStateOrResumeChanged(mLauncher);
     }
 
     private void onStateTransitionEnd(LauncherState state) {
@@ -312,6 +313,7 @@
         }
 
         UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+        UiFactory.onLauncherStateOrResumeChanged(mLauncher);
     }
 
     public void onWindowFocusChanged() {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index aafae10..8788db4 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -23,7 +23,6 @@
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.SearchUiManager.OnScrollRangeChangeListener;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
@@ -39,8 +38,7 @@
  * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
  * closer to top or closer to the page indicator.
  */
-public class AllAppsTransitionController
-        implements OnScrollRangeChangeListener, StateHandler, OnDeviceProfileChangeListener {
+public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
 
     public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
             new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@@ -71,11 +69,11 @@
     private float mShiftRange;      // changes depending on the orientation
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
 
-    private static final float DEFAULT_SHIFT_RANGE = 10;
+    private float mScrollRangeDelta = 0;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
-        mShiftRange = DEFAULT_SHIFT_RANGE;
+        mShiftRange = mLauncher.getDeviceProfile().heightPx;
         mProgress = 1f;
 
         mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
@@ -95,6 +93,7 @@
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         mIsVerticalLayout = dp.isVerticalBarLayout();
+        setScrollRangeDelta(mScrollRangeDelta);
 
         if (mIsVerticalLayout) {
             mAppsView.setAlpha(1);
@@ -205,13 +204,14 @@
 
     public void setupViews(AllAppsContainerView appsView) {
         mAppsView = appsView;
-        mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
     }
 
-    @Override
-    public void onScrollRangeChanged(int scrollRange) {
-        mShiftRange = scrollRange;
-        setProgress(mProgress);
+    /**
+     * Updates the total scroll range but does not update the UI.
+     */
+    public void setScrollRangeDelta(float delta) {
+        mScrollRangeDelta = delta;
+        mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index d8568f8..68193f5 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-import android.support.animation.SpringAnimation;
-import android.support.annotation.NonNull;
 import android.view.KeyEvent;
 
 /**
@@ -30,11 +28,6 @@
     void initialize(AllAppsContainerView containerView);
 
     /**
-     * A {@link SpringAnimation} that will be used when the user flings.
-     */
-    @NonNull SpringAnimation getSpringForFling();
-
-    /**
      * Notifies the search manager to close any active search session.
      */
     void resetSearch();
@@ -44,14 +37,4 @@
      * some UI beforehand.
      */
     void preDispatchKeyEvent(KeyEvent keyEvent);
-
-    void addOnScrollRangeChangeListener(OnScrollRangeChangeListener listener);
-
-    /**
-     * Callback for listening to changes in the vertical scroll range when opening all-apps.
-     */
-    interface OnScrollRangeChangeListener {
-
-        void onScrollRangeChanged(int scrollRange);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index dd80dac..ad61c55 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -23,10 +23,6 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.animation.FloatValueHolder;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.NonNull;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -39,6 +35,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
@@ -55,7 +52,7 @@
  */
 public class AppsSearchContainerLayout extends ExtendedEditText
         implements SearchUiManager, AllAppsSearchBarController.Callbacks,
-        AllAppsStore.OnUpdateListener {
+        AllAppsStore.OnUpdateListener, Insettable {
 
 
     private final Launcher mLauncher;
@@ -64,7 +61,6 @@
 
     private AlphabeticalAppsList mApps;
     private AllAppsContainerView mAppsView;
-    private SpringAnimation mSpring;
 
     public AppsSearchContainerLayout(Context context) {
         this(context, null);
@@ -91,9 +87,6 @@
         spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
                 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
         setHint(spanned);
-
-        // Note: This spring does nothing.
-        mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
     }
 
     @Override
@@ -146,11 +139,6 @@
     }
 
     @Override
-    public @NonNull SpringAnimation getSpringForFling() {
-        return mSpring;
-    }
-
-    @Override
     public void onAppsUpdated() {
         mSearchBarController.refreshSearchResult();
     }
@@ -206,22 +194,15 @@
     }
 
     @Override
-    public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) {
-        mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                DeviceProfile dp = mLauncher.getDeviceProfile();
-                if (!dp.isVerticalBarLayout()) {
-                    Rect insets = dp.getInsets();
-                    int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
-                    MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
-                    int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
-                    listener.onScrollRangeChanged(hotseatBottom - myBot);
-                } else {
-                    listener.onScrollRangeChanged(bottom);
-                }
-            }
-        });
+    public void setInsets(Rect insets) {
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        if (dp.isVerticalBarLayout()) {
+            mLauncher.getAllAppsController().setScrollRangeDelta(0);
+        } else {
+            MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
+            int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
+            mLauncher.getAllAppsController().setScrollRangeDelta(
+                    dp.hotseatBarBottomPaddingPx + myBot);
+        }
     }
 }
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index 04d97a7..a3d02d9 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.anim;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.view.View;
@@ -25,44 +24,24 @@
 /**
  * A convenience class to update a view's visibility state after an alpha animation.
  */
-public class AlphaUpdateListener extends AnimatorListenerAdapter implements AnimatorUpdateListener {
+public class AlphaUpdateListener extends AnimationSuccessListener
+        implements AnimatorUpdateListener {
     private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
 
     private View mView;
-    private boolean mAccessibilityEnabled;
-    private boolean mCanceled = false;
 
-    public AlphaUpdateListener(View v, boolean accessibilityEnabled) {
+    public AlphaUpdateListener(View v) {
         mView = v;
-        mAccessibilityEnabled = accessibilityEnabled;
     }
 
     @Override
     public void onAnimationUpdate(ValueAnimator arg0) {
-        updateVisibility(mView, mAccessibilityEnabled);
-    }
-
-    public static void updateVisibility(View view, boolean accessibilityEnabled) {
-        // We want to avoid the extra layout pass by setting the views to GONE unless
-        // accessibility is on, in which case not setting them to GONE causes a glitch.
-        int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE;
-        if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
-            view.setVisibility(invisibleState);
-        } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
-                && view.getVisibility() != View.VISIBLE) {
-            view.setVisibility(View.VISIBLE);
-        }
+        updateVisibility(mView);
     }
 
     @Override
-    public void onAnimationCancel(Animator animation) {
-        mCanceled = true;
-    }
-
-    @Override
-    public void onAnimationEnd(Animator arg0) {
-        if (mCanceled) return;
-        updateVisibility(mView, mAccessibilityEnabled);
+    public void onAnimationSuccess(Animator animator) {
+        updateVisibility(mView);
     }
 
     @Override
@@ -70,4 +49,13 @@
         // We want the views to be visible for animation, so fade-in/out is visible
         mView.setVisibility(View.VISIBLE);
     }
+
+    public static void updateVisibility(View view) {
+        if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.INVISIBLE) {
+            view.setVisibility(View.INVISIBLE);
+        } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
+                && view.getVisibility() != View.VISIBLE) {
+            view.setVisibility(View.VISIBLE);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 1f11f7e..757edff 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.anim;
 
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
-
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -34,7 +32,7 @@
     public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
         if (view != null) {
             view.setAlpha(alpha);
-            AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext()));
+            AlphaUpdateListener.updateVisibility(view);
         }
     }
 
@@ -64,8 +62,7 @@
                 return;
             }
             ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
-            anim.addListener(new AlphaUpdateListener(
-                    view, isAccessibilityEnabled(view.getContext())));
+            anim.addListener(new AlphaUpdateListener(view));
             anim.setDuration(mDuration).setInterpolator(interpolator);
             mStateAnimator.play(anim);
         }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 112cca5..3270ba2 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -45,7 +45,7 @@
     /**
      * @return a map of active installs to their progress
      */
-    public abstract HashMap<String, Integer> updateAndGetActiveSessionCache();
+    public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
 
     public abstract void onStop();
 
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 1ffd3da..dd17916 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -59,13 +59,13 @@
     }
 
     @Override
-    public HashMap<String, Integer> updateAndGetActiveSessionCache() {
-        HashMap<String, Integer> activePackages = new HashMap<>();
+    public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
+        HashMap<String, SessionInfo> activePackages = new HashMap<>();
         UserHandle user = Process.myUserHandle();
         for (SessionInfo info : getAllVerifiedSessions()) {
             addSessionInfoToCache(info, user);
             if (info.getAppPackageName() != null) {
-                activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
+                activePackages.put(info.getAppPackageName(), info);
                 mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
             }
         }
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index a0912a4..069ec4b 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -129,18 +129,15 @@
             };
 
     public void setup(Launcher launcher, View invalidateDelegate,
-                      int availableSpace, int topPadding) {
+                      int availableSpaceX, int topPadding) {
         mInvalidateDelegate = invalidateDelegate;
         mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
 
         DeviceProfile grid = launcher.getDeviceProfile();
-        final int previewSize = grid.folderIconSizePx;
-        final int previewPadding = grid.folderIconPreviewPadding;
+        previewSize = grid.folderIconSizePx;
 
-        this.previewSize = (previewSize - 2 * previewPadding);
-
-        basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
-        basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+        basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
+        basePreviewOffsetY = topPadding + grid.folderIconOffsetYPx;
 
         // Stroke width is 1dp
         mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 5d99ba0..680c020 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -376,4 +376,12 @@
             last = i;
         }
     }
+
+    /**
+     * @return The diameter of the normalized circle that fits inside of the square (size x size).
+     */
+    public static int getNormalizedCircleSize(int size) {
+        float area = size * size * MAX_CIRCLE_AREA_FACTOR;
+        return (int) Math.round(Math.sqrt((4 * area) / Math.PI));
+    }
 }
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
new file mode 100644
index 0000000..1149b55
--- /dev/null
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.util.MultiHashMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class to send broadcasts to package installers that have:
+ * - Items on the first screen
+ * - Items with an active install session
+ *
+ * The packages are broken down by: folder items, workspace items, hotseat items, and widgets.
+ *
+ * Package installers only receive data for items that they are installing.
+ */
+public class FirstScreenBroadcast {
+
+    private static final String TAG = "FirstScreenBroadcast";
+    private static final boolean DEBUG = false;
+
+    private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS
+            = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS";
+
+    private static final String FOLDER_ITEM_EXTRA = "folderItem";
+    private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem";
+    private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem";
+    private static final String WIDGET_ITEM_EXTRA = "widgetItem";
+
+    private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
+
+    private final MultiHashMap<String, String> mPackagesForInstaller;
+
+    public FirstScreenBroadcast(HashMap<String, SessionInfo> sessionInfoForPackage) {
+        mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
+    }
+
+    /**
+     * @return Map where the key is the package name of the installer, and the value is a list
+     *         of packages with active sessions for that installer.
+     */
+    private MultiHashMap<String, String> getPackagesForInstaller(
+            HashMap<String, SessionInfo> sessionInfoForPackage) {
+        MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
+        for (Map.Entry<String, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
+            packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
+                    entry.getKey());
+        }
+        return packagesForInstaller;
+    }
+
+    /**
+     * Sends a broadcast to all package installers that have items with active sessions on the users
+     * first screen.
+     */
+    public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
+        for (Map.Entry<String, ArrayList<String>> entry : mPackagesForInstaller.entrySet()) {
+            sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems);
+        }
+    }
+
+    /**
+     * @param installerPackageName Package name of the package installer.
+     * @param packages List of packages with active sessions for this package installer.
+     * @param firstScreenItems List of items on the first screen.
+     */
+    private void sendBroadcastToInstaller(Context context, String installerPackageName,
+            List<String> packages, List<ItemInfo> firstScreenItems) {
+        Set<String> folderItems = new HashSet<>();
+        Set<String> workspaceItems = new HashSet<>();
+        Set<String> hotseatItems = new HashSet<>();
+        Set<String> widgetItems = new HashSet<>();
+
+        for (ItemInfo info : firstScreenItems) {
+            if (info instanceof FolderInfo) {
+                FolderInfo folderInfo = (FolderInfo) info;
+                String folderItemInfoPackage;
+                for (ItemInfo folderItemInfo : folderInfo.contents) {
+                    folderItemInfoPackage = getPackageName(folderItemInfo);
+                    if (folderItemInfoPackage != null
+                            && packages.contains(folderItemInfoPackage)) {
+                        folderItems.add(folderItemInfoPackage);
+                    }
+                }
+            }
+
+            String packageName = getPackageName(info);
+            if (packageName == null || !packages.contains(packageName)) {
+                continue;
+            }
+            if (info instanceof LauncherAppWidgetInfo) {
+                widgetItems.add(packageName);
+            } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+                hotseatItems.add(packageName);
+            } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                workspaceItems.add(packageName);
+            }
+        }
+
+        if (DEBUG) {
+            printList(installerPackageName, "Folder item", folderItems);
+            printList(installerPackageName, "Workspace item", workspaceItems);
+            printList(installerPackageName, "Hotseat item", hotseatItems);
+            printList(installerPackageName, "Widget item", widgetItems);
+        }
+
+        context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS)
+                .setPackage(installerPackageName)
+                .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems))
+                .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems))
+                .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems))
+                .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems))
+                .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0,
+                        new Intent(), PendingIntent.FLAG_ONE_SHOT)));
+    }
+
+    private static String getPackageName(ItemInfo info) {
+        String packageName = null;
+        if (info instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+            if (widgetInfo.providerName != null) {
+                packageName = widgetInfo.providerName.getPackageName();
+            }
+        } else if (info.getTargetComponent() != null){
+            packageName = info.getTargetComponent().getPackageName();
+        }
+        return packageName;
+    }
+
+    private static void printList(String packageInstaller, String label, Set<String> packages) {
+        for (String pkg : packages) {
+            Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 5d4a352..0fd9b73 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -209,7 +209,7 @@
 
     /** Filters the set of items who are directly or indirectly (via another container) on the
      * specified screen. */
-    private <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
+    public static <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
             ArrayList<T> allWorkspaceItems,
             ArrayList<T> currentScreenItems,
             ArrayList<T> otherScreenItems) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 9d1ff83..06da843 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -29,6 +30,7 @@
 import android.content.IntentFilter;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.graphics.Bitmap;
 import android.os.Handler;
 import android.os.Process;
@@ -92,6 +94,8 @@
     private final AllAppsList mBgAllAppsList;
     private final BgDataModel mBgDataModel;
 
+    private FirstScreenBroadcast mFirstScreenBroadcast;
+
     private final LoaderResults mResults;
 
     private final LauncherAppsCompat mLauncherApps;
@@ -134,6 +138,22 @@
         }
     }
 
+    private void sendFirstScreenActiveInstallsBroadcast() {
+        ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
+
+        ArrayList<ItemInfo> allItems = new ArrayList<>();
+        synchronized (mBgDataModel) {
+            allItems.addAll(mBgDataModel.workspaceItems);
+            allItems.addAll(mBgDataModel.appWidgets);
+        }
+        long firstScreen = mBgDataModel.workspaceScreens.isEmpty()
+                ? -1 // In this case, we can still look at the items in the hotseat.
+                : mBgDataModel.workspaceScreens.get(0);
+        filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
+                new ArrayList<>() /* otherScreenItems are ignored */);
+        mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+    }
+
     public void run() {
         synchronized (this) {
             // Skip fast if we are already stopped.
@@ -151,6 +171,10 @@
             TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
             mResults.bindWorkspace();
 
+            // Notify the installer packages of packages with active installs on the first screen.
+            TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
+            sendFirstScreenActiveInstallsBroadcast();
+
             // Take a break
             TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
             waitForIdle();
@@ -242,8 +266,9 @@
         synchronized (mBgDataModel) {
             mBgDataModel.clear();
 
-            final HashMap<String, Integer> installingPkgs =
+            final HashMap<String, SessionInfo> installingPkgs =
                     mPackageInstaller.updateAndGetActiveSessionCache();
+            mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
             mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
 
             Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
@@ -511,11 +536,11 @@
                                 }
 
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
-                                    Integer progress = installingPkgs.get(targetPkg);
-                                    if (progress != null) {
-                                        info.setInstallProgress(progress);
-                                    } else {
+                                    SessionInfo si = installingPkgs.get(targetPkg);
+                                    if (si == null) {
                                         info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                                    } else {
+                                        info.setInstallProgress((int) (si.getProgress() * 100));
                                     }
                                 }
 
@@ -605,7 +630,11 @@
                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                             component);
                                     appWidgetInfo.restoreStatus = c.restoreFlag;
-                                    Integer installProgress = installingPkgs.get(component.getPackageName());
+                                    SessionInfo si =
+                                            installingPkgs.get(component.getPackageName());
+                                    Integer installProgress = si == null
+                                            ? null
+                                            : (int) (si.getProgress() * 100);
 
                                     if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
                                         // Restore has started once.
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
index dabd40d..b8bcfed 100644
--- a/src/com/android/launcher3/util/PendingRequestArgs.java
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -57,7 +57,7 @@
 
         mArg1 = parcel.readInt();
         mObjectType = parcel.readInt();
-        mObject = parcel.readParcelable(null);
+        mObject = parcel.readParcelable(getClass().getClassLoader());
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index dc86aec..709a7e5 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -107,7 +107,7 @@
 
     private boolean handleViewClick(View view, int action) {
         if (view.getId() == R.id.wallpaper_button) {
-            mLauncher.onClickWallpaperPicker(null);
+            mLauncher.onClickWallpaperPicker(view);
             logTap(action, ControlType.WALLPAPER_BUTTON);
             close(true);
             return true;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index e0b76fd..be9d5b7 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -38,5 +38,7 @@
 
     public static void onStart(Launcher launcher) { }
 
+    public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
+
     public static void onTrimMemory(Launcher launcher, int level) { }
 }