Merge "Fix crash if AM returns empty task list" into tm-dev
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 3b1f119..a74774c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -31,6 +31,8 @@
  */
 public class AllAppsState extends LauncherState {
 
+    private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
+
     private static final int STATE_FLAGS =
             FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE;
 
@@ -58,7 +60,7 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(0.97f, NO_OFFSET, NO_OFFSET);
+        return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
     }
 
     @Override
@@ -69,7 +71,7 @@
             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
                     .getWorkspaceScaleAndTranslation(launcher);
             return new ScaleAndTranslation(
-                    NO_SCALE,
+                    WORKSPACE_SCALE_FACTOR,
                     overviewScaleAndTranslation.translationX,
                     overviewScaleAndTranslation.translationY);
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index dbee9c1..e874bf0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -25,9 +25,17 @@
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 
 import android.view.MotionEvent;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -70,6 +78,28 @@
      */
     public static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
 
+    // Custom timing for NORMAL -> ALL_APPS on phones only.
+    private static final float ALL_APPS_STATE_TRANSITION = 0.4f;
+
+    // Custom interpolators for NORMAL -> ALL_APPS on phones only.
+    private static final Interpolator LINEAR_EARLY =
+            Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION);
+    private static final Interpolator STEP_TRANSITION =
+            Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator BLUR = LINEAR_EARLY;
+    public static final Interpolator WORKSPACE_FADE = STEP_TRANSITION;
+    public static final Interpolator WORKSPACE_SCALE = LINEAR_EARLY;
+    public static final Interpolator HOTSEAT_FADE = STEP_TRANSITION;
+    public static final Interpolator HOTSEAT_SCALE = LINEAR_EARLY;
+    public static final Interpolator HOTSEAT_TRANSLATE = STEP_TRANSITION;
+    public static final Interpolator SCRIM_FADE = LINEAR_EARLY;
+    public static final Interpolator ALL_APPS_FADE =
+            Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION, 1f);
+    public static final Interpolator ALL_APPS_VERTICAL_PROGRESS =
+            Interpolators.clampToProgress(
+                    Interpolators.mapToProgress(LINEAR, ALL_APPS_STATE_TRANSITION, 1.0f),
+                    ALL_APPS_STATE_TRANSITION, 1.0f);
+
     private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
 
     public PortraitStatesTouchController(Launcher l) {
@@ -127,29 +157,51 @@
 
     private StateAnimationConfig getNormalToAllAppsAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
-        boolean isTablet = mLauncher.getDeviceProfile().isTablet;
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
-                ? INSTANT
-                : Interpolators.clampToProgress(LINEAR,
-                        ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
-                        ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
-        builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(LINEAR,
-                ALL_APPS_SCRIM_VISIBLE_THRESHOLD,
-                ALL_APPS_SCRIM_OPAQUE_THRESHOLD));
+        if (mLauncher.getDeviceProfile().isTablet) {
+            builder.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
+            builder.setInterpolator(ANIM_SCRIM_FADE,
+                    Interpolators.clampToProgress(LINEAR,
+                            ALL_APPS_SCRIM_VISIBLE_THRESHOLD,
+                            ALL_APPS_SCRIM_OPAQUE_THRESHOLD));
+        } else {
+            // TODO(b/231682175): centralize this setup in AllAppsSwipeController.
+            builder.setInterpolator(ANIM_DEPTH, BLUR);
+            builder.setInterpolator(ANIM_WORKSPACE_FADE, WORKSPACE_FADE);
+            builder.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE);
+            builder.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE);
+            builder.setInterpolator(ANIM_HOTSEAT_SCALE, HOTSEAT_SCALE);
+            builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE);
+            builder.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE);
+            builder.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE);
+            builder.setInterpolator(ANIM_VERTICAL_PROGRESS, ALL_APPS_VERTICAL_PROGRESS);
+        }
         return builder;
     }
 
     private StateAnimationConfig getAllAppsToNormalAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
-        boolean isTablet = mLauncher.getDeviceProfile().isTablet;
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
-                ? FINAL_FRAME
-                : Interpolators.clampToProgress(LINEAR,
-                        1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
-                        1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
-        builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(LINEAR,
-                1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
-                1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+        if (mLauncher.getDeviceProfile().isTablet) {
+            builder.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
+            builder.setInterpolator(ANIM_SCRIM_FADE,
+                    Interpolators.clampToProgress(LINEAR,
+                            1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
+                            1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+        } else {
+            // These interpolators are the reverse of the ones used above, so swiping out of All
+            // Apps feels the same as swiping into it.
+            // TODO(b/231682175): centralize this setup in AllAppsSwipeController.
+            builder.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR));
+            builder.setInterpolator(ANIM_WORKSPACE_FADE, Interpolators.reverse(WORKSPACE_FADE));
+            builder.setInterpolator(ANIM_WORKSPACE_SCALE, Interpolators.reverse(WORKSPACE_SCALE));
+            builder.setInterpolator(ANIM_HOTSEAT_FADE, Interpolators.reverse(HOTSEAT_FADE));
+            builder.setInterpolator(ANIM_HOTSEAT_SCALE, Interpolators.reverse(HOTSEAT_SCALE));
+            builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE,
+                    Interpolators.reverse(HOTSEAT_TRANSLATE));
+            builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.reverse(SCRIM_FADE));
+            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.reverse(ALL_APPS_FADE));
+            builder.setInterpolator(ANIM_VERTICAL_PROGRESS,
+                    Interpolators.reverse(ALL_APPS_VERTICAL_PROGRESS));
+        }
         return builder;
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 11bc11f..31c1eff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2045,13 +2045,16 @@
         // Note: There should be at most one log per method call. This is enforced implicitly
         // by using if-else statements.
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
-        if (topView != null && topView.onBackPressed()) {
-            // Handled by the floating view.
-        } else {
-            mStateManager.getState().onBackPressed(this);
+        if (topView == null || !topView.onBackPressed()) {
+            // Not handled by the floating view.
+            onStateBack();
         }
     }
 
+    protected void onStateBack() {
+        mStateManager.getState().onBackPressed(this);
+    }
+
     protected void onScreenOff() {
         // Reset AllApps to its initial state only if we are not in the middle of
         // processing a multi-step drop
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 096e2c8..88e7fc0 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,9 +15,7 @@
  */
 package com.android.launcher3.allapps;
 
-import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.UNSPECIFIED;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
@@ -47,7 +45,6 @@
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -97,8 +94,6 @@
     private AllAppsBackgroundDrawable mEmptySearchBackground;
     private int mEmptySearchBackgroundTopOffset;
 
-    private ArrayList<View> mAutoSizedOverlays = new ArrayList<>();
-
     public AllAppsRecyclerView(Context context) {
         this(context, null);
     }
@@ -172,30 +167,6 @@
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         updateEmptySearchBackgroundBounds();
         updatePoolSize();
-        for (int i = 0; i < mAutoSizedOverlays.size(); i++) {
-            View overlay = mAutoSizedOverlays.get(i);
-            overlay.measure(makeMeasureSpec(w, EXACTLY), makeMeasureSpec(w, EXACTLY));
-            overlay.layout(0, 0, w, h);
-        }
-    }
-
-    /**
-     * Adds an overlay that automatically rescales with the recyclerview.
-     */
-    public void addAutoSizedOverlay(View overlay) {
-        mAutoSizedOverlays.add(overlay);
-        getOverlay().add(overlay);
-        onSizeChanged(getWidth(), getHeight(), getWidth(), getHeight());
-    }
-
-    /**
-     * Clears auto scaling overlay views added by #addAutoSizedOverlay
-     */
-    public void clearAutoSizedOverlays() {
-        for (View v : mAutoSizedOverlays) {
-            getOverlay().remove(v);
-        }
-        mAutoSizedOverlays.clear();
     }
 
     public void onSearchResultsChanged() {
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index c38cf9a..0a77aa7 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -208,4 +208,14 @@
             float upperBound) {
         return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound);
     }
+
+    /**
+     * Returns the reverse of the provided interpolator, following the formula: g(x) = 1 - f(1 - x).
+     * In practice, this means that if f is an interpolator used to model a value animating between
+     * m and n, g is the interpolator to use to obtain the specular behavior when animating from n
+     * to m.
+     */
+    public static Interpolator reverse(Interpolator interpolator) {
+        return t -> 1 - interpolator.getInterpolation(1 - t);
+    }
 }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 9d7fd9a..fb91628 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
@@ -66,6 +67,7 @@
                     EMPHASIZED_DECELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
     public static final Interpolator HOTSEAT_FADE =
             Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION);
+    public static final Interpolator HOTSEAT_SCALE = HOTSEAT_FADE;
     public static final Interpolator HOTSEAT_TRANSLATE =
             Interpolators.clampToProgress(
                     EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
@@ -163,6 +165,7 @@
             config.setInterpolator(ANIM_DEPTH, BLUR);
             config.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE);
             config.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE);
+            config.setInterpolator(ANIM_HOTSEAT_SCALE, HOTSEAT_SCALE);
             config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE);
             config.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE);
             config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index ec921e8..bf35dd8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -32,6 +32,7 @@
 public class AllAppsState extends LauncherState {
 
     private static final float PARALLAX_COEFFICIENT = .125f;
+    private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
 
     private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE;
 
@@ -59,7 +60,7 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(0.97f, NO_OFFSET, NO_OFFSET);
+        return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
     }
 
     @Override
@@ -70,7 +71,7 @@
             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
                     .getWorkspaceScaleAndTranslation(launcher);
             return new ScaleAndTranslation(
-                    NO_SCALE,
+                    WORKSPACE_SCALE_FACTOR,
                     overviewScaleAndTranslation.translationX,
                     overviewScaleAndTranslation.translationY);
         }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index a3b05f6..8a97c6b 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -48,7 +48,6 @@
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.ScreenRecordRule;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
@@ -70,9 +69,6 @@
     private static final String STORE_APP_NAME = "Play Store";
     private static final String GMAIL_APP_NAME = "Gmail";
 
-    @Rule
-    public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
-
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -380,7 +376,6 @@
 
     @Test
     @PortraitLandscape
-    @ScreenRecord
     public void testDragToFolder() {
         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
         // on tablets or phones due to difference in resolution.
@@ -397,6 +392,15 @@
                 workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME));
         assertNull(GMAIL_APP_NAME + " should be moved to a folder.",
                 workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME));
+
+        final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
+        folderIcon = mapIcon.dragToIcon(folderIcon);
+        folder = folderIcon.open();
+        folder.getAppIcon(MAPS_APP_NAME);
+        workspace = folder.close();
+
+        assertNull(MAPS_APP_NAME + " should be moved to a folder.",
+                workspace.tryGetWorkspaceAppIcon(MAPS_APP_NAME));
     }
 
     @Test
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 954af3d..eb7f05b 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -394,6 +394,7 @@
                 launcher,
                 launchable,
                 destSupplier,
+                /* isDecelerating= */ false,
                 () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
                 /* expectDropEvents= */ null);
     }
@@ -404,6 +405,17 @@
             Supplier<Point> dest,
             Runnable expectLongClickEvents,
             @Nullable Runnable expectDropEvents) {
+        dragIconToWorkspace(launcher, launchable, dest, /* isDecelerating */ true,
+                expectLongClickEvents, expectDropEvents);
+    }
+
+    static void dragIconToWorkspace(
+            LauncherInstrumentation launcher,
+            Launchable launchable,
+            Supplier<Point> dest,
+            boolean isDecelerating,
+            Runnable expectLongClickEvents,
+            @Nullable Runnable expectDropEvents) {
         try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
                 "want to drag icon to workspace")) {
             final long downTime = SystemClock.uptimeMillis();
@@ -430,7 +442,7 @@
 
             // targetDest.x is now between 0 and displayX so we found the target page,
             // we just have to put move the icon to the destination and drop it
-            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
+            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
                     downTime, SystemClock.uptimeMillis(), false,
                     LauncherInstrumentation.GestureScope.INSIDE);
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);