Merge "integration test for open settings menu from workspace." into ub-launcher3-master
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 27ac284..bd89626 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
@@ -47,7 +47,7 @@
 
     private static final float RING_EFFECT_RATIO = 0.11f;
 
-    private DeviceProfile mDeviceProfile;
+    private final DeviceProfile mDeviceProfile;
     private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private boolean mIsPinned = false;
     private int mNormalizedIconRadius;
@@ -65,7 +65,7 @@
         mDeviceProfile = Launcher.getLauncher(context).getDeviceProfile();
         mNormalizedIconRadius = IconNormalizer.getNormalizedCircleSize(getIconSize()) / 2;
         setOnClickListener(ItemClickHandler.INSTANCE);
-        setOnFocusChangeListener(Launcher.getLauncher(context).mFocusHandler);
+        setOnFocusChangeListener(Launcher.getLauncher(context).getFocusHandler());
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index ad02de1..32855d7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -18,7 +18,7 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
@@ -286,7 +286,7 @@
                 }
             });
         }
-        if (QUICKSTEP_SPRINGS.get()) {
+        if (UNSTABLE_SPRINGS.get()) {
             mCurrentAnimation.dispatchOnStartWithVelocity(goingToEnd ? 1f : 0f, velocity);
         }
         anim.start();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 1b60404..8b5283e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -973,7 +973,7 @@
         }
         mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
 
-        if (QUICKSTEP_SPRINGS.get()) {
+        if (UNSTABLE_SPRINGS.get()) {
             mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
         }
         mLauncherTransitionController.getAnimationPlayer().start();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 29df5cc..71568b3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
@@ -715,6 +716,7 @@
             pw.println("FeatureFlags:");
             pw.println("  APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
             pw.println("  QUICKSTEP_SPRINGS=" + QUICKSTEP_SPRINGS.get());
+            pw.println("  UNSTABLE_SPRINGS=" + UNSTABLE_SPRINGS.get());
             pw.println("  ADAPTIVE_ICON_WINDOW_ANIM=" + ADAPTIVE_ICON_WINDOW_ANIM.get());
             pw.println("  ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
             pw.println("  ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
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 47bc31a..c836791 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
@@ -31,7 +31,7 @@
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
@@ -1105,13 +1105,13 @@
 
     private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
         addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
-        if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView)
+        if (UNSTABLE_SPRINGS.get() && taskView instanceof TaskView) {
             addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y,
                             MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
                             SpringForce.STIFFNESS_MEDIUM,
                             0, -taskView.getHeight()),
                     duration, LINEAR, anim);
-        else {
+        } else {
             addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
                     duration, LINEAR, anim);
         }
@@ -1185,7 +1185,7 @@
                 }
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
                 if (scrollDiff != 0) {
-                    if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) {
+                    if (UNSTABLE_SPRINGS.get() && child instanceof TaskView) {
                         addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X,
                                 MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
                                 SpringForce.STIFFNESS_MEDIUM,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 99b2a81..d5ce734 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -25,7 +25,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.TimeInterpolator;
@@ -277,7 +277,7 @@
     private void handleFirstSwipeToOverview(final ValueAnimator animator,
             final long expectedDuration, final LauncherState targetState, final float velocity,
             final boolean isFling) {
-        if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+        if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
                 && targetState == OVERVIEW) {
             mFinishFastOnSecondTouch = true;
         } else  if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d8c4c5c..a7bb9ee 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -307,7 +307,7 @@
     // Request id for any pending activity result
     protected int mPendingActivityRequestCode = -1;
 
-    public ViewGroupFocusHelper mFocusHandler;
+    private ViewGroupFocusHelper mFocusHandler;
 
     private RotationHelper mRotationHelper;
 
@@ -617,6 +617,10 @@
         return mRotationHelper;
     }
 
+    public ViewGroupFocusHelper getFocusHandler() {
+        return mFocusHandler;
+    }
+
     public LauncherStateManager getStateManager() {
         return mStateManager;
     }
@@ -1740,7 +1744,7 @@
         getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
 
         // Create the view
-        FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo);
+        FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout, folderInfo);
         mWorkspace.addInScreen(newFolder, folderInfo);
         // Force measure the new folder icon
         CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
@@ -2101,7 +2105,7 @@
                     break;
                 }
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
-                    view = FolderIcon.fromXml(R.layout.folder_icon, this,
+                    view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this,
                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (FolderInfo) item);
                     break;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f96e735..9a3a379 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -83,7 +83,6 @@
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.WorkspaceTouchListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -2547,7 +2546,7 @@
                     view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                    view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
+                    view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, mLauncher, cellLayout,
                             (FolderInfo) info);
                     break;
                 default:
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 08ce9c2..0681919 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -11,10 +11,12 @@
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.util.FloatProperty;
 import android.view.animation.Interpolator;
 
@@ -183,8 +185,11 @@
     }
 
     public Animator createSpringAnimation(float... progressValues) {
-        return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
-                SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
+        if (UNSTABLE_SPRINGS.get()) {
+            return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
+                    SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
+        }
+        return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
     }
 
     private void setAlphas(LauncherState toState, AnimationConfig config,
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 003ca82..81dcba3 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -85,7 +85,10 @@
             "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
 
     public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
-            false, "Enable springs for quickstep animations");
+            true, "Enable springs for quickstep animations");
+
+    public static final TogglableFlag UNSTABLE_SPRINGS = new TogglableFlag("UNSTABLE_SPRINGS",
+            false, "Enable unstable springs for quickstep animations");
 
     public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
             "ADAPTIVE_ICON_WINDOW_ANIM", true,
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index f59a192..52d8f7f 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -423,7 +423,7 @@
                 mFolderName.setHint(suggestName[0]);
                 mFolderName.setText(suggestName[0]);
                 mInfo.title = suggestName[0];
-                animateOpen();
+                animateOpen(mInfo.contents, 0, true);
                 mFolderName.showKeyboard();
                 mFolderName.displayCompletions(
                         Arrays.asList(suggestName).subList(1, suggestName.length));
@@ -519,6 +519,15 @@
      * is played.
      */
     private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
+        animateOpen(items, pageNo, false);
+    }
+
+    /**
+     * Opens the user folder described by the specified tag. The opening of the folder
+     * is animated relative to the specified View. If the View is null, no animation
+     * is played.
+     */
+    private void animateOpen(List<WorkspaceItemInfo> items, int pageNo, boolean skipUserEventLog) {
         Folder openFolder = getOpen(mLauncher);
         if (openFolder != null && openFolder != this) {
             // Close any open folder before opening a folder.
@@ -565,10 +574,13 @@
                 mState = STATE_OPEN;
                 announceAccessibilityChanges();
 
-                mLauncher.getUserEventDispatcher().logActionOnItem(
+                if (!skipUserEventLog) {
+                    mLauncher.getUserEventDispatcher().logActionOnItem(
                         Touch.TAP,
                         Direction.NONE,
                         ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
+                }
+
 
                 mContent.setFocusOnFirstChild();
             }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index f322061..8c56823 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -67,6 +67,7 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.IconLabelDotView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
@@ -79,7 +80,7 @@
  */
 public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
 
-    @Thunk Launcher mLauncher;
+    @Thunk ActivityContext mActivity;
     @Thunk Folder mFolder;
     private FolderInfo mInfo;
 
@@ -153,7 +154,21 @@
         mDotParams = new DotRenderer.DrawParams();
     }
 
-    public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
+    public static FolderIcon inflateFolderAndIcon(int resId, Launcher launcher, ViewGroup group,
+            FolderInfo folderInfo) {
+        Folder folder = Folder.fromXml(launcher);
+        folder.setDragController(launcher.getDragController());
+
+        FolderIcon icon = inflateIcon(resId, launcher, group, folderInfo);
+        folder.setFolderIcon(icon);
+        folder.bind(folderInfo);
+        icon.setFolder(folder);
+
+        icon.setOnFocusChangeListener(launcher.getFocusHandler());
+        return icon;
+    }
+
+    public static FolderIcon inflateIcon(int resId, ActivityContext activity, ViewGroup group,
             FolderInfo folderInfo) {
         @SuppressWarnings("all") // suppress dead code warning
         final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
@@ -163,7 +178,7 @@
                     "is dependent on this");
         }
 
-        DeviceProfile grid = launcher.getWallpaperDeviceProfile();
+        DeviceProfile grid = activity.getWallpaperDeviceProfile();
         FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
                 .inflate(resId, group, false);
 
@@ -177,27 +192,27 @@
         icon.setTag(folderInfo);
         icon.setOnClickListener(ItemClickHandler.INSTANCE);
         icon.mInfo = folderInfo;
-        icon.mLauncher = launcher;
+        icon.mActivity = activity;
         icon.mDotRenderer = grid.mDotRendererWorkSpace;
-        icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
+
+        icon.setContentDescription(
+                group.getContext().getString(R.string.folder_name_format, folderInfo.title));
 
         // Keep the notification dot up to date with the sum of all the content's dots.
         FolderDotInfo folderDotInfo = new FolderDotInfo();
         for (WorkspaceItemInfo si : folderInfo.contents) {
-            folderDotInfo.addDotInfo(launcher.getDotInfoForItem(si));
+            folderDotInfo.addDotInfo(activity.getDotInfoForItem(si));
         }
         icon.setDotInfo(folderDotInfo);
 
-        Folder folder = Folder.fromXml(launcher);
-        folder.setDragController(launcher.getDragController());
-        folder.setFolderIcon(icon);
-        folder.bind(folderInfo);
-        icon.setFolder(folder);
-        icon.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
+        icon.setAccessibilityDelegate(activity.getAccessibilityDelegate());
+
+        icon.mPreviewVerifier = new FolderGridOrganizer(activity.getDeviceProfile().inv);
+        icon.mPreviewVerifier.setFolderInfo(folderInfo);
+        icon.updatePreviewItems(false);
 
         folderInfo.addListener(icon);
 
-        icon.setOnFocusChangeListener(launcher.mFocusHandler);
         return icon;
     }
 
@@ -225,9 +240,6 @@
 
     private void setFolder(Folder folder) {
         mFolder = folder;
-        mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
-        mPreviewVerifier.setFolderInfo(mFolder.getInfo());
-        updatePreviewItems(false);
     }
 
     private boolean willAcceptItem(ItemInfo item) {
@@ -309,14 +321,15 @@
         // Typically, the animateView corresponds to the DragView; however, if this is being done
         // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
         // will not have a view to animate
-        if (animateView != null) {
-            DragLayer dragLayer = mLauncher.getDragLayer();
+        if (animateView != null && mActivity instanceof Launcher) {
+            final Launcher launcher = (Launcher) mActivity;
+            DragLayer dragLayer = launcher.getDragLayer();
             Rect from = new Rect();
             dragLayer.getViewRectRelativeToSelf(animateView, from);
             Rect to = finalRect;
             if (to == null) {
                 to = new Rect();
-                Workspace workspace = mLauncher.getWorkspace();
+                Workspace workspace = launcher.getWorkspace();
                 // Set cellLayout and this to it's final state to compute final animation locations
                 workspace.setFinalTransitionTransform();
                 float scaleX = getScaleX();
@@ -382,7 +395,7 @@
             String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
                 Executors.UI_HELPER_EXECUTOR.post(() -> {
-                    mLauncher.getFolderNameProvider().getSuggestedFolderName(
+                    launcher.getFolderNameProvider().getSuggestedFolderName(
                             getContext(), mInfo.contents, suggestedNameOut);
                     showFinalView(finalIndex, item, suggestedNameOut);
                 });
@@ -547,7 +560,7 @@
         if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
             Rect iconBounds = mDotParams.iconBounds;
             BubbleTextView.getIconBounds(this, iconBounds,
-                    mLauncher.getWallpaperDeviceProfile().iconSizePx);
+                    mActivity.getWallpaperDeviceProfile().iconSizePx);
             float iconScale = (float) mBackground.previewSize / iconBounds.width();
             Utilities.scaleRectAboutCenter(iconBounds, iconScale);
 
@@ -605,7 +618,7 @@
     @Override
     public void onAdd(WorkspaceItemInfo item, int rank) {
         boolean wasDotted = mDotInfo.hasDot();
-        mDotInfo.addDotInfo(mLauncher.getDotInfoForItem(item));
+        mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
         updateDotScale(wasDotted, isDotted);
         invalidate();
@@ -615,7 +628,7 @@
     @Override
     public void onRemove(WorkspaceItemInfo item) {
         boolean wasDotted = mDotInfo.hasDot();
-        mDotInfo.subtractDotInfo(mLauncher.getDotInfoForItem(item));
+        mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
         updateDotScale(wasDotted, isDotted);
         invalidate();
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 5b3a05e..27aa43e 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -37,10 +37,10 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -94,7 +94,8 @@
     public PreviewItemManager(FolderIcon icon) {
         mContext = icon.getContext();
         mIcon = icon;
-        mIconSize = Launcher.getLauncher(mContext).getDeviceProfile().folderChildIconSizePx;
+        mIconSize = ActivityContext.lookupContext(
+                mContext).getDeviceProfile().folderChildIconSizePx;
     }
 
     /**
@@ -132,7 +133,7 @@
             mTotalWidth = totalSize;
             mPrevTopPadding = mIcon.getPaddingTop();
 
-            mIcon.mBackground.setup(mIcon.mLauncher, mIcon.mLauncher, mIcon, mTotalWidth,
+            mIcon.mBackground.setup(mIcon.getContext(), mIcon.mActivity, mIcon, mTotalWidth,
                     mIcon.getPaddingTop());
             mIcon.mPreviewLayoutRule.init(mIcon.mBackground.previewSize, mIntrinsicIconSize,
                     Utilities.isRtl(mIcon.getResources()));
@@ -152,7 +153,7 @@
     }
 
     private PreviewItemDrawingParams getFinalIconParams(PreviewItemDrawingParams params) {
-        float iconSize = mIcon.mLauncher.getDeviceProfile().iconSizePx;
+        float iconSize = mIcon.mActivity.getDeviceProfile().iconSizePx;
 
         final float scale = iconSize / mReferenceDrawable.getIntrinsicWidth();
         final float trans = (mIcon.mBackground.previewSize - iconSize) / 2;
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 0c5535f..bfe7351 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -49,6 +49,7 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FolderInfo;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -63,6 +64,7 @@
 import com.android.launcher3.WorkspaceLayoutManager;
 import com.android.launcher3.allapps.SearchUiManager;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.BitmapRenderer;
@@ -239,6 +241,12 @@
             addInScreenFromBind(icon, info);
         }
 
+        private void inflateAndAddFolder(FolderInfo info) {
+            FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
+                    info);
+            addInScreenFromBind(folderIcon, info);
+        }
+
         private void dispatchVisibilityAggregated(View view, boolean isVisible) {
             // Similar to View.dispatchVisibilityAggregated implementation.
             final boolean thisVisible = view.getVisibility() == VISIBLE;
@@ -288,7 +296,7 @@
                             inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
                             break;
                         case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                            // TODO: for folder implementation here.
+                            inflateAndAddFolder((FolderInfo) itemInfo);
                             break;
                         default:
                             break;
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index f40f976..f470edb 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -23,7 +23,7 @@
 import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
 import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.animation.Animator;
@@ -434,7 +434,7 @@
         updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
                 targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, velocity);
-        if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) {
+        if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 56eca6d..1c8f095 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -22,6 +22,7 @@
 
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
     <uses-permission android:name="android.permission.READ_LOGS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner"/>
diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
deleted file mode 100644
index 6e6734d..0000000
--- a/tests/tapl/com/android/launcher3/tapl/Folder.java
+++ /dev/null
@@ -1,35 +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.tapl;
-
-import android.widget.FrameLayout;
-
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
-import androidx.test.uiautomator.UiObject2;
-
-/**
- * App folder in workspace/
- */
-public final class Folder {
-    Folder(LauncherInstrumentation launcher, UiObject2 icon) {
-    }
-
-    static BySelector getSelector(String folderName, LauncherInstrumentation launcher) {
-        return By.clazz(FrameLayout.class).desc(folderName).pkg(launcher.getLauncherPackageName());
-    }
-}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 217f696..de6fdb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -184,13 +184,8 @@
                 .authority(testProviderAuthority)
                 .build();
 
-        try {
-            mDevice.executeShellCommand("pm grant " + testPackage +
-                    " android.permission.WRITE_SECURE_SETTINGS");
-        } catch (IOException e) {
-            fail(e.toString());
-        }
-
+        mInstrumentation.getUiAutomation().grantRuntimePermission(
+                testPackage, "android.permission.WRITE_SECURE_SETTINGS");
 
         PackageManager pm = getContext().getPackageManager();
         ProviderInfo pi = pm.resolveContentProvider(
@@ -346,14 +341,11 @@
 
     private String getSystemHealthMessage() {
         final String testPackage = getContext().getPackageName();
-        try {
-            mDevice.executeShellCommand("pm grant " + testPackage +
-                    " android.permission.READ_LOGS");
-            mDevice.executeShellCommand("pm grant " + testPackage +
-                    " android.permission.PACKAGE_USAGE_STATS");
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+
+        mInstrumentation.getUiAutomation().grantRuntimePermission(
+                testPackage, "android.permission.READ_LOGS");
+        mInstrumentation.getUiAutomation().grantRuntimePermission(
+                testPackage, "android.permission.PACKAGE_USAGE_STATS");
 
         return mSystemHealthSupplier != null
                 ? mSystemHealthSupplier.apply(START_TIME)
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 3299d5d..af7e552 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -183,12 +183,6 @@
                 mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
     }
 
-    @NonNull
-    public Folder getHotseatFolder(String appName) {
-        return new Folder(mLauncher, mLauncher.waitForObjectInContainer(
-                mHotseat, Folder.getSelector(appName, mLauncher)));
-    }
-
     static void dragIconToWorkspace(
             LauncherInstrumentation launcher, Launchable launchable, Point dest,
             String longPressIndicator) {