Merge "perform accessbility focus when the recyclerview doesn't gain focus after fragment replacement in SettingsActivity" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
index 17a3d91..0254340 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
@@ -66,6 +66,9 @@
     //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
     private static final int APPTARGET_ACTION_UNPIN = 4;
 
+    private static final String APP_LOCATION_HOTSEAT = "hotseat";
+    private static final String APP_LOCATION_WORKSPACE = "workspace";
+
     private static final String PREDICTION_CLIENT = "hotseat";
 
     private DropTarget.DragObject mDragObject;
@@ -132,7 +135,7 @@
             }
 
             WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
-            if (isPredictedIcon(child)) {
+            if (isPredictedIcon(child) && child.isEnabled()) {
                 PredictedAppIcon icon = (PredictedAppIcon) child;
                 icon.applyFromWorkspaceItem(predictedItem);
                 icon.finishBinding();
@@ -204,17 +207,24 @@
 
     private Bundle getAppPredictionContextExtra() {
         Bundle bundle = new Bundle();
-        ViewGroup vg = mHotseat.getShortcutsAndWidgets();
+        bundle.putParcelableArrayList(APP_LOCATION_HOTSEAT,
+                getPinnedAppTargetsInViewGroup((mHotseat.getShortcutsAndWidgets())));
+        bundle.putParcelableArrayList(APP_LOCATION_WORKSPACE, getPinnedAppTargetsInViewGroup(
+                mLauncher.getWorkspace().getScreenWithId(
+                        Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets()));
+        return bundle;
+    }
+
+    private ArrayList<AppTarget> getPinnedAppTargetsInViewGroup(ViewGroup viewGroup) {
         ArrayList<AppTarget> pinnedApps = new ArrayList<>();
-        for (int i = 0; i < vg.getChildCount(); i++) {
-            View child = vg.getChildAt(i);
+        for (int i = 0; i < viewGroup.getChildCount(); i++) {
+            View child = viewGroup.getChildAt(i);
             if (isPinnedIcon(child)) {
                 WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) child.getTag();
                 pinnedApps.add(getAppTargetFromItemInfo(itemInfo));
             }
         }
-        bundle.putParcelableArrayList("pinned_apps", pinnedApps);
-        return bundle;
+        return pinnedApps;
     }
 
     private void setPredictedApps(List<AppTarget> appTargets) {
@@ -252,7 +262,7 @@
         ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
         icon.pin(workspaceItemInfo);
         AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
-        notifyItemAction(appTarget, AppTargetEvent.ACTION_PIN);
+        notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
     }
 
     private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
@@ -300,6 +310,7 @@
             int rank = ((WorkspaceItemInfo) icon.getTag()).rank;
             outlines.add(new PredictedAppIcon.PredictedIconOutlineDrawing(
                     mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank), icon));
+            icon.setEnabled(false);
             icon.animate().scaleY(0).scaleX(0).setListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
@@ -312,9 +323,10 @@
     }
 
 
-    private void notifyItemAction(AppTarget target, int action) {
+    private void notifyItemAction(AppTarget target, String location, int action) {
         if (mAppPredictor != null) {
-            mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target, action).build());
+            mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target,
+                    action).setLaunchLocation(location).build());
         }
     }
 
@@ -336,10 +348,18 @@
         }
         ItemInfo dragInfo = mDragObject.dragInfo;
         if (dragInfo instanceof WorkspaceItemInfo && dragInfo.getTargetComponent() != null) {
+            AppTarget appTarget = getAppTargetFromItemInfo(dragInfo);
+            if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
+                notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
+            }
+            if (!isInFirstPage(dragInfo) && isInFirstPage(mDragObject.originalDragInfo)) {
+                notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+            }
             if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
-                notifyItemAction(getAppTargetFromItemInfo(dragInfo), AppTargetEvent.ACTION_PIN);
-            } else if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
-                notifyItemAction(getAppTargetFromItemInfo(dragInfo), APPTARGET_ACTION_UNPIN);
+                notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
+            }
+            if (isInFirstPage(dragInfo) && !isInFirstPage(mDragObject.originalDragInfo)) {
+                notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
             }
         }
         mDragObject = null;
@@ -412,7 +432,7 @@
             return false;
         }
         ItemInfo info = (ItemInfo) view.getTag();
-        return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && (
+        return info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION && (
                 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                         || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
     }
@@ -421,6 +441,11 @@
         return itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
     }
 
+    private static boolean isInFirstPage(ItemInfo itemInfo) {
+        return itemInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
+                && itemInfo.screenId == Workspace.FIRST_SCREEN_ID;
+    }
+
     private static AppTarget getAppTargetFromItemInfo(ItemInfo info) {
         if (info.getTargetComponent() == null) return null;
         ComponentName cn = info.getTargetComponent();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index e41c75a..1dcbffb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -40,11 +40,12 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.views.DoubleShadowBubbleTextView;
 
 /**
  * A BubbleTextView with a ring around it's drawable
  */
-public class PredictedAppIcon extends BubbleTextView {
+public class PredictedAppIcon extends DoubleShadowBubbleTextView {
 
     private static final float RING_EFFECT_RATIO = 0.12f;
 
@@ -97,6 +98,7 @@
         applyFromWorkspaceItem(info);
         setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
         mIsPinned = true;
+        ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
         invalidate();
     }
 
@@ -111,6 +113,7 @@
             }
             return true;
         });
+        ((CellLayout.LayoutParams) getLayoutParams()).canReorder = false;
         setTextVisibility(false);
         verifyHighRes();
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index b14da5c..630dd70 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -430,20 +430,6 @@
         final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
         anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
 
-            // Alpha interpolates between [1, 0] between progress values [start, end]
-            final float start = 0f;
-            final float end = 0.85f;
-
-            private float getWindowAlpha(float progress) {
-                if (progress <= start) {
-                    return 1f;
-                }
-                if (progress >= end) {
-                    return 0f;
-                }
-                return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
-            }
-
             @Override
             public void onUpdate(RectF currentRect, float progress) {
                 homeAnim.setPlayFraction(progress);
@@ -484,6 +470,24 @@
         return anim;
     }
 
+    /**
+     * @param progress The progress of the animation to the home screen.
+     * @return The current alpha to set on the animating app window.
+     */
+    protected float getWindowAlpha(float progress) {
+        // Alpha interpolates between [1, 0] between progress values [start, end]
+        final float start = 0f;
+        final float end = 0.85f;
+
+        if (progress <= start) {
+            return 1f;
+        }
+        if (progress >= end) {
+            return 0f;
+        }
+        return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+    }
+
     public interface Factory {
 
         BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index c939de8..034f158 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
 import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -34,6 +36,7 @@
 import android.graphics.RectF;
 import android.os.Bundle;
 import android.util.ArrayMap;
+import android.view.MotionEvent;
 
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
@@ -97,6 +100,7 @@
     private final boolean mContinuingLastGesture;
     private final boolean mRunningOverHome;
     private final boolean mSwipeUpOverHome;
+    private boolean mTouchedHomeDuringTransition;
 
     private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
     private RunningWindowAnim mFinishAnimation;
@@ -105,13 +109,14 @@
             GestureState gestureState, InputConsumerController inputConsumer,
             boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
         super(context, deviceState, gestureState, inputConsumer);
-        mLauncherAlpha.value = 1;
 
         mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
         mContinuingLastGesture = continuingLastGesture;
         mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
         mSwipeUpOverHome = mRunningOverHome && !mInQuickSwitchMode;
 
+        // Keep the home launcher invisible until we decide to land there.
+        mLauncherAlpha.value = mRunningOverHome ? 1 : 0;
         if (mSwipeUpOverHome) {
             mAppWindowAnimationHelper.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
         } else {
@@ -198,15 +203,27 @@
     @Override
     protected InputConsumer createNewInputProxyHandler() {
         // Just consume all input on the active task
-        return InputConsumer.NO_OP;
+        return new InputConsumer() {
+            @Override
+            public int getType() {
+                return InputConsumer.TYPE_NO_OP;
+            }
+
+            @Override
+            public void onMotionEvent(MotionEvent ev) {
+                mTouchedHomeDuringTransition = true;
+            }
+        };
     }
 
     @Override
     public void onMotionPauseChanged(boolean isPaused) {
         if (!mInQuickSwitchMode) {
             mIsMotionPaused = isPaused;
-            mLauncherAlpha.animateToValue(mLauncherAlpha.value, isPaused ? 0 : 1)
-                    .setDuration(150).start();
+            if (mSwipeUpOverHome) {
+                mLauncherAlpha.animateToValue(mLauncherAlpha.value, isPaused ? 0 : 1)
+                        .setDuration(150).start();
+            }
             performHapticFeedback();
         }
     }
@@ -315,7 +332,14 @@
                     // Send a home intent to clear the task stack
                     mContext.startActivity(mGestureState.getHomeIntent());
                 } else {
-                    mRecentsAnimationController.finish(true, null, true);
+                    mRecentsAnimationController.finish(true, () -> {
+                        if (!mTouchedHomeDuringTransition) {
+                            // If the user hasn't interacted with the screen during the transition,
+                            // send a home intent so launcher can go to the default home screen.
+                            // (If they are trying to touch something, we don't want to interfere.)
+                            mContext.startActivity(mGestureState.getHomeIntent());
+                        }
+                    }, true);
                 }
                 break;
             }
@@ -389,6 +413,8 @@
             };
 
             if (mGestureState.getEndTarget() == HOME && !mRunningOverHome) {
+                mRecentsAnimationController.enableInputProxy(mInputConsumer,
+                        this::createNewInputProxyHandler);
                 RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
                 anim.addAnimatorListener(endListener);
                 anim.start(mEndVelocityPxPerMs);
@@ -445,11 +471,18 @@
             @Override
             public AnimatorPlaybackController createActivityAnimationToHome() {
                 AnimatorSet anim = new AnimatorSet();
-                anim.play(mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1));
+                Animator fadeInLauncher = mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1);
+                fadeInLauncher.setInterpolator(ACCEL_2);
+                anim.play(fadeInLauncher);
                 anim.setDuration(duration);
                 return AnimatorPlaybackController.wrap(anim, duration);
             }
         };
         return createWindowAnimationToHome(startProgress, factory);
     }
+
+    @Override
+    protected float getWindowAlpha(float progress) {
+        return 1 - ACCEL_1_5.getInterpolation(progress);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 6e7214e..b602cea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -177,6 +177,7 @@
     private final float mFastFlingVelocity;
     private final RecentsModel mModel;
     private final int mTaskTopMargin;
+    private final int mTaskBottomMargin;
     private final ClearAllButton mClearAllButton;
     private final Rect mClearAllButtonDeadZoneRect = new Rect();
     private final Rect mTaskViewDeadZoneRect = new Rect();
@@ -343,6 +344,8 @@
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         mTaskTopMargin = getResources()
                 .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+        mTaskBottomMargin = getResources().getDimensionPixelSize(
+                R.dimen.task_thumbnail_bottom_margin);
         mSquaredTouchSlop = squaredTouchSlop(context);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -696,6 +699,7 @@
         mTaskHeight = mTempRect.height();
 
         mTempRect.top -= mTaskTopMargin;
+        mTempRect.bottom += mTaskBottomMargin;
         setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
                 dp.widthPx - mInsets.right - mTempRect.right,
                 dp.heightPx - mInsets.bottom - mTempRect.bottom);
@@ -1467,7 +1471,7 @@
 
         // Set the pivot points to match the task preview center
         setPivotY(((mInsets.top + getPaddingTop() + mTaskTopMargin)
-                + (getHeight() - mInsets.bottom - getPaddingBottom())) / 2);
+                + (getHeight() - mInsets.bottom - getPaddingBottom() - mTaskBottomMargin)) / 2);
         setPivotX(((mInsets.left + getPaddingLeft())
                 + (getWidth() - mInsets.right - getPaddingRight())) / 2);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index a1775f4..0bfde64 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -540,8 +540,11 @@
             }
 
             addView(view, indexToAdd);
-            ((LayoutParams) view.getLayoutParams()).gravity =
+            LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
+            layoutParams.gravity =
                     Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+            layoutParams.bottomMargin =
+                    ((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin;
             view.setAlpha(mFooterAlpha);
             mFooters[index] = new FooterWrapper(view);
             if (shouldAnimateEntry) {
@@ -618,10 +621,12 @@
     private static final class TaskOutlineProvider extends ViewOutlineProvider {
 
         private final int mMarginTop;
+        private final int mMarginBottom;
         private FullscreenDrawParams mFullscreenParams;
 
         TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) {
             mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+            mMarginBottom = res.getDimensionPixelSize(R.dimen.task_thumbnail_bottom_margin);
             mFullscreenParams = fullscreenParams;
         }
 
@@ -636,7 +641,7 @@
             outline.setRoundRect(0,
                     (int) (mMarginTop * scale),
                     (int) ((insets.left + view.getWidth() + insets.right) * scale),
-                    (int) ((insets.top + view.getHeight() + insets.bottom) * scale),
+                    (int) ((insets.top + view.getHeight() + insets.bottom - mMarginBottom) * scale),
                     mFullscreenParams.mCurrentDrawnCornerRadius);
         }
     }
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 60cfa0c..7a36416 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -25,7 +25,8 @@
         android:id="@+id/snapshot"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/task_thumbnail_top_margin"/>
+        android:layout_marginTop="@dimen/task_thumbnail_top_margin"
+        android:layout_marginBottom="@dimen/task_thumbnail_bottom_margin"/>
 
     <com.android.quickstep.views.IconView
         android:id="@+id/icon"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 78424ca..82833ea 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -18,6 +18,8 @@
 
     <dimen name="task_thumbnail_top_margin">24dp</dimen>
     <dimen name="task_thumbnail_half_top_margin">12dp</dimen>
+    <!-- Can be overridden in overlays. -->
+    <dimen name="task_thumbnail_bottom_margin">0dp</dimen>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
     <!-- For screens without rounded corners -->
     <dimen name="task_corner_radius_small">2dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 5aa4388..c86139b 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -229,6 +229,31 @@
             params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1);
             // Perform wellbeing call .
             remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params);
+
+            synchronized (mModelLock) {
+                // Remove the entries for requested packages, and then update the fist with what we
+                // got from service
+                Arrays.stream(packageNames).forEach(mPackageToActionId::remove);
+
+                // The result consists of sub-bundles, each one is per a remote action. Each
+                // sub-bundle has a RemoteAction and a list of packages to which the action applies.
+                for (String actionId :
+                        remoteActionBundle.getStringArray(EXTRA_ACTIONS)) {
+                    final Bundle actionBundle = remoteActionBundle.getBundle(actionId);
+                    mActionIdMap.put(actionId,
+                            actionBundle.getParcelable(EXTRA_ACTION));
+
+                    final String[] packagesForAction =
+                            actionBundle.getStringArray(EXTRA_PACKAGES);
+                    if (DEBUG || mIsInTest) {
+                        Log.d(TAG, "....actionId: " + actionId + ", packages: " + String.join(", ",
+                                packagesForAction));
+                    }
+                    for (String packageName : packagesForAction) {
+                        mPackageToActionId.put(packageName, actionId);
+                    }
+                }
+            }
         } catch (DeadObjectException e) {
             Log.i(TAG, "retrieveActions(): DeadObjectException");
             return false;
@@ -237,31 +262,6 @@
             if (mIsInTest) throw new RuntimeException(e);
             return true;
         }
-
-        synchronized (mModelLock) {
-            // Remove the entries for requested packages, and then update the fist with what we
-            // got from service
-            Arrays.stream(packageNames).forEach(mPackageToActionId::remove);
-
-            // The result consists of sub-bundles, each one is per a remote action. Each sub-bundle
-            // has a RemoteAction and a list of packages to which the action applies.
-            for (String actionId :
-                    remoteActionBundle.getStringArray(EXTRA_ACTIONS)) {
-                final Bundle actionBundle = remoteActionBundle.getBundle(actionId);
-                mActionIdMap.put(actionId,
-                        actionBundle.getParcelable(EXTRA_ACTION));
-
-                final String[] packagesForAction =
-                        actionBundle.getStringArray(EXTRA_PACKAGES);
-                if (DEBUG || mIsInTest) {
-                    Log.d(TAG, "....actionId: " + actionId + ", packages: " + String.join(", ",
-                            packagesForAction));
-                }
-                for (String packageName : packagesForAction) {
-                    mPackageToActionId.put(packageName, actionId);
-                }
-            }
-        }
         if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): finished");
         return true;
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 965b5f0..1e03b05 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -16,46 +16,13 @@
 
 package com.android.launcher3.uioverrides;
 
-import android.app.Activity;
 import android.app.Person;
 import android.content.pm.ShortcutInfo;
-import android.util.Base64;
 
 import com.android.launcher3.Utilities;
-import com.android.systemui.shared.system.ActivityCompat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.util.zip.Deflater;
 
 public class ApiWrapper {
 
-    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
-        if (!Utilities.IS_DEBUG_DEVICE) {
-            return false;
-        }
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
-            return false;
-        }
-
-        Deflater deflater = new Deflater();
-        deflater.setInput(out.toByteArray());
-        deflater.finish();
-
-        out.reset();
-        byte[] buffer = new byte[1024];
-        while (!deflater.finished()) {
-            int count = deflater.deflate(buffer); // returns the generated code... index
-            out.write(buffer, 0, count);
-        }
-
-        writer.println("--encoded-view-dump-v0--");
-        writer.println(Base64.encodeToString(
-                out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
-        return true;
-    }
-
     public static Person[] getPersons(ShortcutInfo si) {
         Person[] persons = si.getPersons();
         return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 2e118b4..c47bb4a 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -105,6 +105,7 @@
         }
 
         float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+        float bottomMargin = res.getDimension(R.dimen.task_thumbnail_bottom_margin);
         float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
 
         // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
@@ -113,7 +114,7 @@
         int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
 
         float availableHeight = launcherVisibleHeight
-                - topIconMargin - extraVerticalSpace - paddingVert;
+                - topIconMargin - extraVerticalSpace - paddingVert - bottomMargin;
         float availableWidth = launcherVisibleWidth - paddingHorz;
 
         float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index 62915f2..310d43c 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -27,7 +27,7 @@
     mockito-robolectric-prebuilt \
     truth-prebuilt
 LOCAL_JAVA_LIBRARIES := \
-    platform-robolectric-3.6.1-prebuilt
+    platform-robolectric-4.3-prebuilt
 
 LOCAL_JAVA_RESOURCE_DIRS := resources config
 
@@ -54,4 +54,4 @@
 
 LOCAL_ROBOTEST_TIMEOUT := 36000
 
-include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
+include prebuilts/misc/common/robolectric/4.3/run_robotests.mk
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index 5b6d94d..f7e05a4 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -1,10 +1,13 @@
 package com.android.launcher3.model;
 
+import static com.android.launcher3.shadows.ShadowLooperExecutor.reinitializeStaticExecutors;
+
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.util.ReflectionHelpers.setField;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,6 +32,7 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.cache.CachingLogic;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.pm.PackageInstallerCompat;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.TestLauncherProvider;
 
@@ -53,7 +57,7 @@
 public class BaseModelUpdateTaskTestCase {
 
     public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
-    private TestLauncherProvider mProvider;
+    public TestLauncherProvider provider;
 
     public Context targetContext;
     public UserHandle myUser;
@@ -71,9 +75,11 @@
     @Before
     public void setUp() throws Exception {
         ShadowLog.stream = System.out;
+        reinitializeStaticExecutors();
+        setField(PackageInstallerCompat.class, null, "sInstance", null);
 
-        mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
-        ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
+        provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
+        ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
 
         callbacks = mock(Callbacks.class);
         appState = mock(LauncherAppState.class);
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
new file mode 100644
index 0000000..9e4a43c
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 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 static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.robolectric.Shadows.shadowOf;
+import static org.robolectric.util.ReflectionHelpers.setField;
+
+import android.content.ComponentName;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
+import android.net.Uri;
+import android.provider.Settings;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.shadows.LShadowLauncherApps;
+import com.android.launcher3.shadows.LShadowUserManager;
+import com.android.launcher3.shadows.ShadowLooperExecutor;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
+import org.robolectric.shadows.ShadowPackageManager;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Tests for layout parser for remote layout
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {LShadowUserManager.class, LShadowLauncherApps.class, ShadowLooperExecutor.class})
+@LooperMode(Mode.PAUSED)
+public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
+
+    private static final String SETTINGS_APP = "com.android.settings";
+    private static final String TEST_PROVIDER_AUTHORITY =
+            DefaultLayoutProviderTest.class.getName().toLowerCase();
+
+    private static final int BITMAP_SIZE = 10;
+    private static final int GRID_SIZE = 4;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        InvariantDeviceProfile.INSTANCE.initializeForTesting(idp);
+        CustomWidgetManager.INSTANCE.initializeForTesting(mock(CustomWidgetManager.class));
+
+        idp.numRows = idp.numColumns = idp.numHotseatIcons = GRID_SIZE;
+        idp.iconBitmapSize = BITMAP_SIZE;
+
+        provider.setAllowLoadDefaultFavorites(true);
+        Settings.Secure.putString(targetContext.getContentResolver(),
+                "launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
+
+        ShadowPackageManager spm = shadowOf(targetContext.getPackageManager());
+        spm.addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
+                TEST_PROVIDER_AUTHORITY;
+        spm.addActivityIfNotPresent(new ComponentName(SETTINGS_APP, SETTINGS_APP));
+    }
+
+    @After
+    public void cleanup() {
+        InvariantDeviceProfile.INSTANCE.initializeForTesting(null);
+        CustomWidgetManager.INSTANCE.initializeForTesting(null);
+    }
+
+    @Test
+    public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
+        writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
+                .putApp(SETTINGS_APP, SETTINGS_APP));
+
+        // Verify one item in hotseat
+        assertEquals(1, bgDataModel.workspaceItems.size());
+        ItemInfo info = bgDataModel.workspaceItems.get(0);
+        assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
+        assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPLICATION, info.itemType);
+    }
+
+    @Test
+    public void testCustomProfileLoaded_with_folder() throws Exception {
+        writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
+                .addApp(SETTINGS_APP, SETTINGS_APP)
+                .addApp(SETTINGS_APP, SETTINGS_APP)
+                .addApp(SETTINGS_APP, SETTINGS_APP)
+                .build());
+
+        // Verify folder
+        assertEquals(1, bgDataModel.workspaceItems.size());
+        ItemInfo info = bgDataModel.workspaceItems.get(0);
+        assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
+        assertEquals(3, ((FolderInfo) info).contents.size());
+    }
+
+    @Test
+    public void testCustomProfileLoaded_with_widget() throws Exception {
+        String pendingAppPkg = "com.test.pending";
+
+        // Add a dummy session info so that the widget exists
+        SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+        params.setAppPackageName(pendingAppPkg);
+
+        PackageInstaller installer = targetContext.getPackageManager().getPackageInstaller();
+        int sessionId = installer.createSession(params);
+        SessionInfo sessionInfo = installer.getSessionInfo(sessionId);
+        setField(sessionInfo, "installerPackageName", "com.test");
+        setField(sessionInfo, "appIcon", BitmapInfo.LOW_RES_ICON);
+
+        writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
+                .putWidget(pendingAppPkg, "DummyWidget", 2, 2));
+
+        // Verify widget
+        assertEquals(1, bgDataModel.appWidgets.size());
+        ItemInfo info = bgDataModel.appWidgets.get(0);
+        assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET, info.itemType);
+        assertEquals(2, info.spanX);
+        assertEquals(2, info.spanY);
+    }
+
+    private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        builder.build(new OutputStreamWriter(bos));
+
+        Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, targetContext);
+        shadowOf(targetContext.getContentResolver()).registerInputStream(layoutUri,
+                new ByteArrayInputStream(bos.toByteArray()));
+
+        LoaderResults results = new LoaderResults(appState, bgDataModel, allAppsList, 0,
+                new WeakReference<>(callbacks));
+        LoaderTask task = new LoaderTask(appState, allAppsList, bgDataModel, results);
+        Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
new file mode 100644
index 0000000..204ec9b
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 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.shadows;
+
+import static org.robolectric.util.ReflectionHelpers.ClassParameter;
+import static org.robolectric.util.ReflectionHelpers.callConstructor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArraySet;
+
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowLauncherApps;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Extension of {@link ShadowLauncherApps} with missing shadow methods
+ */
+@Implements(value = LauncherApps.class)
+public class LShadowLauncherApps extends ShadowLauncherApps {
+
+    public final ArraySet<PackageUserKey> disabledApps = new ArraySet<>();
+    public final ArraySet<ComponentKey> disabledActivities = new ArraySet<>();
+
+    @Implementation
+    @Override
+    protected List<ShortcutInfo> getShortcuts(LauncherApps.ShortcutQuery query, UserHandle user) {
+        try {
+            return super.getShortcuts(query, user);
+        } catch (UnsupportedOperationException e) {
+            return Collections.emptyList();
+        }
+    }
+
+    @Implementation
+    protected boolean isPackageEnabled(String packageName, UserHandle user) {
+        return !disabledApps.contains(new PackageUserKey(packageName, user));
+    }
+
+    @Implementation
+    protected boolean isActivityEnabled(ComponentName component, UserHandle user) {
+        return !disabledActivities.contains(new ComponentKey(component, user));
+    }
+
+    @Implementation
+    protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
+        ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
+                .resolveActivity(intent, 0);
+        return getLauncherActivityInfo(ri.activityInfo);
+    }
+
+    public LauncherActivityInfo getLauncherActivityInfo(ActivityInfo activityInfo) {
+        return callConstructor(LauncherActivityInfo.class,
+                ClassParameter.from(Context.class, RuntimeEnvironment.application),
+                ClassParameter.from(ActivityInfo.class, activityInfo),
+                ClassParameter.from(UserHandle.class, Process.myUserHandle()));
+    }
+
+    @Implementation
+    public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        return RuntimeEnvironment.application.getPackageManager()
+                .getApplicationInfo(packageName, flags);
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
new file mode 100644
index 0000000..edf8edb
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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.shadows;
+
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.SparseBooleanArray;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowUserManager;
+
+/**
+ * Extension of {@link ShadowUserManager} with missing shadow methods
+ */
+@Implements(value = UserManager.class)
+public class LShadowUserManager extends ShadowUserManager {
+
+    private final SparseBooleanArray mQuietUsers = new SparseBooleanArray();
+    private final SparseBooleanArray mLockedUsers = new SparseBooleanArray();
+
+    @Implementation
+    protected boolean isQuietModeEnabled(UserHandle userHandle) {
+        return mQuietUsers.get(userHandle.hashCode());
+    }
+
+    public void setQuietModeEnabled(UserHandle userHandle, boolean enabled) {
+        mQuietUsers.put(userHandle.hashCode(), enabled);
+    }
+
+    @Implementation
+    protected boolean isUserUnlocked(UserHandle userHandle) {
+        return !mLockedUsers.get(userHandle.hashCode());
+    }
+
+    public void setUserLocked(UserHandle userHandle, boolean enabled) {
+        mLockedUsers.put(userHandle.hashCode(), enabled);
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
new file mode 100644
index 0000000..d56de3c
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 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.shadows;
+
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+
+import static org.robolectric.shadow.api.Shadow.invokeConstructor;
+import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
+import static org.robolectric.util.ReflectionHelpers.setField;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.LooperExecutor;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
+ */
+@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
+public class ShadowLooperExecutor {
+
+    // Keep reference to all created Loopers so they can be torn down after test
+    private static Set<LooperExecutor> executors =
+            Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
+
+    @RealObject private LooperExecutor realExecutor;
+
+    @Implementation
+    protected void __constructor__(Looper looper) {
+        invokeConstructor(LooperExecutor.class, realExecutor, from(Looper.class, looper));
+        executors.add(realExecutor);
+    }
+
+    /**
+     * Re-initializes any executor which may have been reset when a test finished
+     */
+    public static void reinitializeStaticExecutors() {
+        for (LooperExecutor executor : new ArrayList<>(executors)) {
+            setField(executor, "mHandler",
+                    new Handler(createAndStartNewLooper(executor.getThread().getName())));
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
similarity index 100%
rename from tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
rename to robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
diff --git a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
index a9c1a7c..7e873e8 100644
--- a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -10,6 +10,8 @@
  */
 public class TestLauncherProvider extends LauncherProvider {
 
+    private boolean mAllowLoadDefaultFavorites;
+
     @Override
     public boolean onCreate() {
         return true;
@@ -18,18 +20,26 @@
     @Override
     protected synchronized void createDbIfNotExists() {
         if (mOpenHelper == null) {
-            mOpenHelper = new MyDatabaseHelper(getContext());
+            mOpenHelper = new MyDatabaseHelper(getContext(), mAllowLoadDefaultFavorites);
         }
     }
 
+    public void setAllowLoadDefaultFavorites(boolean allowLoadDefaultFavorites) {
+        mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
+    }
+
     public SQLiteDatabase getDb() {
         createDbIfNotExists();
         return mOpenHelper.getWritableDatabase();
     }
 
     private static class MyDatabaseHelper extends DatabaseHelper {
-        public MyDatabaseHelper(Context context) {
+
+        private final boolean mAllowLoadDefaultFavorites;
+
+        MyDatabaseHelper(Context context, boolean allowLoadDefaultFavorites) {
             super(context, null);
+            mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
             initIds();
         }
 
@@ -39,7 +49,11 @@
         }
 
         @Override
-        protected void onEmptyDbCreated() { }
+        protected void onEmptyDbCreated() {
+            if (mAllowLoadDefaultFavorites) {
+                super.onEmptyDbCreated();
+            }
+        }
 
         @Override
         protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 382bfdf..f319ae1 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -35,13 +35,11 @@
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
-import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
 
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
@@ -305,13 +303,6 @@
         void onMultiWindowModeChanged(boolean isInMultiWindowMode);
     }
 
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!ApiWrapper.dumpActivity(this, writer)) {
-            super.dump(prefix, fd, writer, args);
-        }
-    }
-
     protected void dumpMisc(String prefix, PrintWriter writer) {
         writer.println(prefix + "deviceProfile isTransposed="
                 + getDeviceProfile().isVerticalBarLayout());
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 3347b2a..bd48aec 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -113,6 +113,7 @@
     public void onDrop(DragObject d, DragOptions options) {
         if (canRemove(d.dragInfo)) {
             mLauncher.getModelWriter().prepareToUndoDelete();
+            d.dragInfo.container = NO_ID;
         }
         super.onDrop(d, options);
     }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 42927ea..67fe038 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -484,8 +484,6 @@
      */
     private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
         Context ctx = getContext();
-        InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
-
         String authority = Settings.Secure.getString(ctx.getContentResolver(),
                 "launcher3.layout.provider");
         if (TextUtils.isEmpty(authority)) {
@@ -497,13 +495,7 @@
             Log.e(TAG, "No provider found for authority " + authority);
             return null;
         }
-        Uri uri = new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
-                .appendQueryParameter("version", "1")
-                .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
-                .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
-                .appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
-                .build();
-
+        Uri uri = getLayoutUri(authority, ctx);
         try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
             // Read the full xml so that we fail early in case of any IO error.
             String layout = new String(IOUtils.toByteArray(in));
@@ -520,6 +512,16 @@
         }
     }
 
+    public static Uri getLayoutUri(String authority, Context ctx) {
+        InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
+        return new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
+                .appendQueryParameter("version", "1")
+                .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
+                .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
+                .appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
+                .build();
+    }
+
     private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
         int defaultLayout = idp.defaultLayoutId;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d445bc9..816d710 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1641,7 +1641,9 @@
             return false;
         }
 
-        boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo);
+        boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo
+                && ((WorkspaceItemInfo) dropOverView.getTag()).container
+                != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
         boolean willBecomeShortcut =
                 (info.itemType == ITEM_TYPE_APPLICATION ||
                         info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f4b705e..ae30380 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -69,16 +69,8 @@
      *
      * To add a new flag that can be toggled through the flags UI:
      *
-     * 1. Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
+     * Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
      *    and set a default value for the flag. This will be the default value on Debug builds.
-     *
-     * 2. Add your flag to mTogglableFlags.
-     *
-     * 3. Create a getter method (an 'is' method) for the flag by copying an existing one.
-     *
-     * 4. Create a getter method with the same name in the release flags copy of FeatureFlags.java.
-     *    This should returns a constant (true/false). This will be the value of the flag used on
-     *    release builds.
      */
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final TogglableFlag PROMISE_APPS_IN_ALL_APPS = new TogglableFlag(
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 33da582..f59a192 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -417,7 +417,8 @@
      * Show suggested folder title.
      */
     public void showSuggestedTitle(String[] suggestName) {
-        if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && mInfo.contents.size() == 2) {
+        if (FeatureFlags.FOLDER_NAME_SUGGEST.get()
+                && TextUtils.isEmpty(mFolderName.getText().toString())) {
             if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
                 mFolderName.setHint(suggestName[0]);
                 mFolderName.setText(suggestName[0]);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 3a4085c..2754cfc 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -42,6 +42,8 @@
 import android.util.MutableInt;
 import android.util.TimingLogger;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
@@ -271,7 +273,8 @@
         this.notify();
     }
 
-    private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
+    @VisibleForTesting
+    void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
         final Context context = mApp.getContext();
         final ContentResolver contentResolver = context.getContentResolver();
         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 5407ea3..6fd147a 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -16,20 +16,13 @@
 
 package com.android.launcher3.uioverrides;
 
-import android.app.Activity;
 import android.app.Person;
 import android.content.pm.ShortcutInfo;
 
 import com.android.launcher3.Utilities;
 
-import java.io.PrintWriter;
-
 public class ApiWrapper {
 
-    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
-        return false;
-    }
-
     public static Person[] getPersons(ShortcutInfo si) {
         return Utilities.EMPTY_PERSON_ARRAY;
     }
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
deleted file mode 100644
index 1d89d6e..0000000
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * Copyright (C) 2019 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.ui;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.testcomponent.TestCommandProvider;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.rule.ShellCommandRule;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.OutputStreamWriter;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
-
-    @Rule
-    public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
-
-    private static final String SETTINGS_APP = "com.android.settings";
-
-    private Context mContext;
-    private String mAuthority;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        mContext = InstrumentationRegistry.getContext();
-
-        PackageManager pm = mTargetContext.getPackageManager();
-        ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
-                TestCommandProvider.class), 0);
-        mAuthority = pi.authority;
-    }
-
-    @Test
-    public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
-        writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
-
-        // Launch the home activity
-        mDevice.pressHome();
-
-        mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
-    }
-
-    @Test
-    public void testCustomProfileLoaded_with_widget() throws Exception {
-        // A non-restored widget with no config screen gets restored automatically.
-        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
-
-        writeLayout(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
-                .putWidget(info.getComponent().getPackageName(),
-                        info.getComponent().getClassName(), 2, 2));
-
-        // Launch the home activity
-        mDevice.pressHome();
-
-        // Verify widget present
-        assertTrue("Widget is not present",
-                mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
-    }
-
-    @Test
-    public void testCustomProfileLoaded_with_folder() throws Exception {
-        writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
-                .addApp(SETTINGS_APP, SETTINGS_APP)
-                .addApp(SETTINGS_APP, SETTINGS_APP)
-                .addApp(SETTINGS_APP, SETTINGS_APP)
-                .build());
-
-        // Launch the home activity
-        mDevice.pressHome();
-
-        mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
-    }
-
-    @After
-    public void cleanup() throws Exception {
-        mDevice.executeShellCommand("settings delete secure launcher3.layout.provider");
-    }
-
-    private void writeLayout(LauncherLayoutBuilder builder) throws Exception {
-        mDevice.executeShellCommand("settings put secure launcher3.layout.provider " + mAuthority);
-        ParcelFileDescriptor pfd = mTargetContext.getContentResolver().openFileDescriptor(
-                Uri.parse("content://" + mAuthority + "/launcher_layout"), "w");
-
-        try (OutputStreamWriter writer = new OutputStreamWriter(new AutoCloseOutputStream(pfd))) {
-            builder.build(writer);
-        }
-        clearLauncherData();
-    }
-}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index ccf98ae..727d757 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -558,6 +558,7 @@
                 dumpViewHierarchy();
                 log(action = "swiping up to home from " + getVisibleStateMessage());
                 final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
+                        || isFallbackOverview()
                         ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
 
                 try (LauncherInstrumentation.Closable c = addContextLayer(action)) {