Merge "Add a quick scrub threshold" into ub-launcher3-edmonton
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bab2cd7..c76ad83 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -21,6 +21,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3" >
 
+    <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="28"/>
     <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 0dc5d7c..9c4b618 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -7,7 +7,6 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="start|top"
-    android:fontFamily="sans-serif-medium"
     android:text="@string/recents_clear_all"
     android:textColor="?attr/workspaceTextColor"
     android:visibility="invisible"
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 7da50c8..5a216f6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
 import android.view.View;
@@ -47,10 +46,6 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
-            launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
-        }
-
         AbstractFloatingView.closeAllOpenViews(launcher);
         dispatchWindowStateChanged(launcher);
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 61422e0..f87f006 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
@@ -59,10 +58,6 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
-            launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
-        }
-
         RecentsView rv = launcher.getOverviewPanel();
         rv.setOverviewStateEnabled(true);
         AbstractFloatingView.closeAllOpenViews(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index cb83a0d..06099b9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -21,6 +21,8 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
 
 import android.content.Context;
 
@@ -28,6 +30,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.TouchController;
@@ -89,6 +92,55 @@
         recents.reset();
     }
 
+    public static void onCreate(Launcher launcher) {
+        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+            launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+                @Override
+                public void onStateSetImmediately(LauncherState state) {
+                }
+
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
+                            .isSwipeUpGestureEnabled();
+                    LauncherState prevState = launcher.getStateManager().getLastState();
+
+                    if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+                            && finalState == ALL_APPS && prevState == NORMAL))) {
+                        launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+                        launcher.getStateManager().removeStateListener(this);
+                    }
+                }
+            });
+        }
+
+        if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+            launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+                @Override
+                public void onStateSetImmediately(LauncherState state) {
+                }
+
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    LauncherState prevState = launcher.getStateManager().getLastState();
+
+                    if (finalState == ALL_APPS && prevState == OVERVIEW) {
+                        launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+                        launcher.getStateManager().removeStateListener(this);
+                    }
+                }
+            });
+        }
+    }
+
     public static void onStart(Context context) {
         RecentsModel model = RecentsModel.getInstance(context);
         if (model != null) {
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 14867ab..f3a0e4f 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -23,9 +23,9 @@
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.TextView;
+import android.widget.Button;
 
-public class ClearAllButton extends TextView {
+public class ClearAllButton extends Button {
     RecentsView mRecentsView;
 
     public ClearAllButton(Context context, @Nullable AttributeSet attrs) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 6e70a55..7a04dcd 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -41,7 +41,6 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -315,52 +314,25 @@
         final int childCount = getChildCount();
         if (mShowEmptyMessage || childCount == 0) return 0;
 
-        // Current visible coordinate of the end of the oldest task.
         final View lastChild = getChildAt(childCount - 1);
+
+        // Current visible coordinate of the end of the oldest task.
         final int carouselCurrentEnd =
                 (mIsRtl ? lastChild.getLeft() : lastChild.getRight()) - getScrollX();
 
-        // As the end (let's call it E aka carouselCurrentEnd) of the carousel moves over Clear
-        // all button, the button changes trasparency.
-        // fullAlphaX and zeroAlphaX are the points of the 100% and 0% alpha correspondingly.
-        // Alpha changes linearly between 100% and 0% as E moves through this range. It doesn't
-        // change outside of the range.
+        // Visible button-facing end of a centered task.
+        final int centeredTaskEnd = mIsRtl ?
+                getPaddingLeft() + mInsets.left :
+                getWidth() - getPaddingRight() - mInsets.right;
 
-        // Once E hits the border of the Clear-All button that looks towards the most recent
-        // task, the whole button is uncovered, and it should have alpha 100%.
-        final float fullAlphaX = mIsRtl ?
-                mClearAllButton.getX() + mClearAllButton.getWidth() :
-                mClearAllButton.getX();
-
-        // X coordinate of the carousel scrolled as far as possible in the direction towards the
-        // button. Logically, the button is "behind" the least recent task. This is the
-        // coordinate of the end of the least recent task in the carousel just after opening,
-        // with the most recent task in the center, and the rest of tasks go from that point
-        // towards and potentially behind the button.
-        final int carouselMotionLimit = getScrollForPage(childCount - 1) - getScrollForPage(0) +
-                (mIsRtl ?
-                        getPaddingLeft() + mInsets.left :
-                        getWidth() - getPaddingRight() - mInsets.right);
-
-        // The carousel might not be able to ever cover a part of the Clear-all button. Then
-        // always show the button as 100%. Technically, this check also prevents dividing by zero
-        // or getting a negative transparency ratio.
-        if (mIsRtl ? carouselMotionLimit >= fullAlphaX : carouselMotionLimit <= fullAlphaX) {
-            return 1;
-        }
-
-        // If the carousel is able to cover the button completely, we make the button completely
-        // transparent when E hits the border of the button farthest from the most recent task.
-        // Or, the carousel may not be able to move that far towards the button so it completely
-        // covers the it. Then we set the motion limit position of the carousel as the point
-        // where the button reaches 0 alpha.
-        final float zeroAlphaX = mIsRtl ?
-                Math.max(mClearAllButton.getX(), carouselMotionLimit) :
-                Math.min(mClearAllButton.getX() + mClearAllButton.getWidth(), carouselMotionLimit);
+        // The distance of the carousel travel during which the alpha changes from 0 to 1. This
+        // is the motion between the oldest task in its centered position and the oldest task
+        // scrolled to the end.
+        final int alphaChangeRange = (mIsRtl ? 0 : mMaxScrollX) - getScrollForPage(childCount - 1);
 
         return Utilities.boundToRange(
-                (zeroAlphaX - carouselCurrentEnd) /
-                        (zeroAlphaX - fullAlphaX), 0, 1);
+                ((float) (centeredTaskEnd - carouselCurrentEnd)) /
+                        alphaChangeRange, 0, 1);
     }
 
     private void updateClearAllButtonAlpha() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index af4faa8..db817e7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -283,6 +283,7 @@
         mDragController = new DragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new LauncherStateManager(this);
+        UiFactory.onCreate(this);
 
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 8311ab9..87ee076 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1171,11 +1171,19 @@
                     mNextPage = getPageNearestToCenterOfScreen(unscaledScrollX);
                     int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
                     int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
-                    if (mSettleOnPageInFreeScroll && unscaledScrollX > firstPageScroll
-                            && unscaledScrollX < lastPageScroll) {
-                        // Make sure we land directly on a page. If flinging past one of the ends,
-                        // don't change the velocity as it will get stopped at the end anyway.
-                        mScroller.setFinalX((int) (getScrollForPage(mNextPage) * getScaleX()));
+                    if (mSettleOnPageInFreeScroll && unscaledScrollX > 0
+                            && unscaledScrollX < mMaxScrollX) {
+                        // If scrolling ends in the half of the added space that is closer to the
+                        // end, settle to the end. Otherwise snap to the nearest page.
+                        // If flinging past one of the ends, don't change the velocity as it will
+                        // get stopped at the end anyway.
+                        final int finalX = unscaledScrollX < firstPageScroll / 2 ?
+                                0 :
+                                unscaledScrollX > (lastPageScroll + mMaxScrollX) / 2 ?
+                                        mMaxScrollX :
+                                        getScrollForPage(mNextPage);
+
+                        mScroller.setFinalX((int) (finalX * getScaleX()));
                         // Ensure the scroll/snap doesn't happen too fast;
                         int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
                                 - mScroller.getDuration();
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index f73916c..8f1c8df 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -42,7 +42,7 @@
  */
 public class DiscoveryBounce extends AbstractFloatingView {
 
-    private static final long DELAY_MS = 200;
+    private static final long DELAY_MS = 450;
 
     public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
     public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index bd1a96e..b8cd035 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -40,6 +40,8 @@
 
     public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
 
+    public static void onCreate(Launcher launcher) { }
+
     public static void onStart(Launcher launcher) { }
 
     public static void onLauncherStateOrResumeChanged(Launcher launcher) { }