Merge "Go back to previous state when hitting back from discovery bounce" into ub-launcher3-edmonton
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fde22eb..b7c5793 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -72,7 +72,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|navigation"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 065663d..cab20a3 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -155,6 +155,7 @@
     CONFIRM = 4;        // Indicates thata confirmation screen was accepted
     STOP = 5;           // Indicates onStop() was called (screen time out, power off)
     RECENTS_BUTTON = 6; // Indicates that Recents button was pressed
+    RESUME = 7;         // Indicates onResume() was called
   }
 
   optional Type type = 1;
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index ac38906..778866d 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -54,7 +54,7 @@
             android:stateNotNeeded="true"
             android:theme="@style/LauncherTheme"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity="" />
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 9c4b618..25615e0 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -11,4 +11,5 @@
     android:textColor="?attr/workspaceTextColor"
     android:visibility="invisible"
     android:textSize="14sp"
+    android:importantForAccessibility="no"
 />
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 80ee577..2e31ef2 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -127,13 +127,6 @@
 
     private RemoteAnimationProvider mRemoteAnimationProvider;
 
-    private final AnimatorListenerAdapter mReapplyStateListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mLauncher.getStateManager().reapplyState();
-        }
-    };
-
     private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationStart(Animator animation) {
@@ -260,7 +253,13 @@
             launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
 
             // Make sure recents gets fixed up by resetting task alphas and scales, etc.
-            windowAnimEndListener = mReapplyStateListener;
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mLauncher.getStateManager().moveToRestState();
+                    mLauncher.getStateManager().reapplyState();
+                }
+            };
         } else {
             AnimatorPlaybackController controller =
                     mLauncher.getStateManager()
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 28b06fb..abb479d 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -63,6 +63,7 @@
     private final BaseActivity mActivity;
 
     private boolean mInQuickScrub;
+    private boolean mWaitingForTaskLaunch;
     private int mQuickScrubSection;
     private boolean mStartedFromHome;
     private boolean mFinishedTransitionToQuickScrub;
@@ -79,11 +80,11 @@
     }
 
     public void onQuickScrubStart(boolean startingFromHome, ActivityControlHelper controlHelper) {
+        prepareQuickScrub(TAG);
         mInQuickScrub = true;
         mStartedFromHome = startingFromHome;
         mQuickScrubSection = 0;
         mFinishedTransitionToQuickScrub = false;
-        mOnFinishedTransitionToQuickScrubRunnable = null;
         mActivityControlHelper = controlHelper;
 
         snapToNextTaskIfAvailable();
@@ -99,11 +100,17 @@
         Runnable launchTaskRunnable = () -> {
             TaskView taskView = mRecentsView.getPageAt(page);
             if (taskView != null) {
+                mWaitingForTaskLaunch = true;
                 taskView.launchTask(true, (result) -> {
                     if (!result) {
                         taskView.notifyTaskLaunchFailed(TAG);
                         breakOutOfQuickScrub();
+                    } else {
+                        mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
+                                LauncherLogProto.Action.Direction.NONE, page,
+                                TaskUtils.getComponentKeyForTask(taskView.getTask().key));
                     }
+                    mWaitingForTaskLaunch = false;
                 }, taskView.getHandler());
             } else {
                 breakOutOfQuickScrub();
@@ -123,9 +130,19 @@
                 mOnFinishedTransitionToQuickScrubRunnable = launchTaskRunnable;
             }
         }
-        mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
-                LauncherLogProto.Action.Direction.NONE, page,
-                TaskUtils.getComponentKeyForTask(mRecentsView.getPageAt(page).getTask().key));
+    }
+
+    /**
+     * Initializes the UI for quick scrub, returns true if success.
+     */
+    public boolean prepareQuickScrub(String tag) {
+        if (mWaitingForTaskLaunch || mInQuickScrub) {
+            Log.d(tag, "Waiting for last scrub to finish, will skip this interaction");
+            return false;
+        }
+        mOnFinishedTransitionToQuickScrubRunnable = null;
+        mRecentsView.setNextPageSwitchRunnable(null);
+        return true;
     }
 
     /**
@@ -166,9 +183,11 @@
 
     public void onFinishedTransitionToQuickScrub() {
         mFinishedTransitionToQuickScrub = true;
-        if (mOnFinishedTransitionToQuickScrubRunnable != null) {
-            mOnFinishedTransitionToQuickScrubRunnable.run();
-            mOnFinishedTransitionToQuickScrubRunnable = null;
+        Runnable action = mOnFinishedTransitionToQuickScrubRunnable;
+        // Clear the runnable before executing it, to prevent potential recursion.
+        mOnFinishedTransitionToQuickScrubRunnable = null;
+        if (action != null) {
+            action.run();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index d7fad43..2b0c98f 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -98,12 +98,13 @@
         if (v.getTag() instanceof ItemInfo) {
             ItemInfo itemInfo = (ItemInfo) v.getTag();
             ComponentName componentName = itemInfo.getTargetComponent();
+            int userId = itemInfo.user.getIdentifier();
             if (componentName != null) {
                 for (int i = 0; i < recentsView.getChildCount(); i++) {
                     TaskView taskView = recentsView.getPageAt(i);
                     if (recentsView.isTaskViewVisible(taskView)) {
-                        Task task = taskView.getTask();
-                        if (componentName.equals(task.key.getComponent())) {
+                        Task.TaskKey key = taskView.getTask().key;
+                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
                             return taskView;
                         }
                     }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 458f9f5..aecb66c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -349,12 +349,20 @@
                 return;
             }
             if (interactionType == INTERACTION_QUICK_SCRUB) {
+                if (!mQuickScrubController.prepareQuickScrub(TAG)) {
+                    mInvalidated = true;
+                    return;
+                }
                 OverviewCallbacks.get(mActivity).closeAllWindows();
                 ActivityManagerWrapper.getInstance()
                         .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
 
                 mStartPending = true;
                 Runnable action = () -> {
+                    if (!mQuickScrubController.prepareQuickScrub(TAG)) {
+                        mInvalidated = true;
+                        return;
+                    }
                     mActivityHelper.onQuickInteractionStart(mActivity, null, true);
                     mQuickScrubController.onQuickScrubProgress(mLastProgress);
                     mStartPending = false;
@@ -384,7 +392,7 @@
         @Override
         public void onQuickScrubProgress(float progress) {
             mLastProgress = progress;
-            if (mInvalidated || mEndPending) {
+            if (mInvalidated || mStartPending) {
                 return;
             }
             mQuickScrubController.onQuickScrubProgress(progress);
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 807dae8..84b2176 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -109,6 +109,8 @@
     private static final int STATE_CAPTURE_SCREENSHOT = 1 << 14;
     private static final int STATE_SCREENSHOT_CAPTURED = 1 << 15;
 
+    private static final int STATE_RESUME_LAST_TASK = 1 << 16;
+
     private static final int LAUNCHER_UI_STATES =
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
             | STATE_LAUNCHER_STARTED;
@@ -139,6 +141,7 @@
             "STATE_QUICK_SCRUB_END",
             "STATE_CAPTURE_SCREENSHOT",
             "STATE_SCREENSHOT_CAPTURED",
+            "STATE_RESUME_LAST_TASK",
     };
 
     public static final long MAX_SWIPE_DURATION = 350;
@@ -187,6 +190,7 @@
     private boolean mGestureStarted;
     private int mLogAction = Touch.SWIPE;
     private float mCurrentQuickScrubProgress;
+    private boolean mQuickScrubBlocked;
 
     private @InteractionType int mInteractionType = INTERACTION_NORMAL;
 
@@ -239,9 +243,12 @@
 
         mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
                 this::sendRemoteAnimationsToAnimationFactory);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_SCALED_CONTROLLER_APP,
+
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
+                this::resumeLastTaskForQuickstep);
+        mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
                 this::resumeLastTask);
+
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                         | STATE_ACTIVITY_MULTIPLIER_COMPLETE
                         | STATE_CAPTURE_SCREENSHOT,
@@ -258,9 +265,6 @@
                         | STATE_GESTURE_COMPLETED,
                 this::setupLauncherUiAfterSwipeUpAnimation);
 
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
-                this::reset);
-
         mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                 this::invalidateHandlerWithLauncher);
@@ -657,9 +661,15 @@
     }
 
     @UiThread
+    private void resumeLastTaskForQuickstep() {
+        setStateOnUiThread(STATE_RESUME_LAST_TASK);
+        doLogGesture(false /* toLauncher */);
+        reset();
+    }
+
+    @UiThread
     private void resumeLastTask() {
         mRecentsAnimationWrapper.finish(false /* toHome */, null);
-        doLogGesture(false /* toLauncher */);
     }
 
     public void reset() {
@@ -760,6 +770,11 @@
     }
 
     private void onQuickScrubStart() {
+        if (!mQuickScrubController.prepareQuickScrub(TAG)) {
+            mQuickScrubBlocked = true;
+            setStateOnUiThread(STATE_RESUME_LAST_TASK | STATE_HANDLER_INVALIDATED);
+            return;
+        }
         if (mLauncherTransitionController != null) {
             mLauncherTransitionController.getAnimationPlayer().end();
             mLauncherTransitionController = null;
@@ -793,12 +808,16 @@
     }
 
     private void onFinishedTransitionToQuickScrub() {
+        if (mQuickScrubBlocked) {
+            return;
+        }
         mQuickScrubController.onFinishedTransitionToQuickScrub();
     }
 
     public void onQuickScrubProgress(float progress) {
         mCurrentQuickScrubProgress = progress;
-        if (Looper.myLooper() != Looper.getMainLooper() || mQuickScrubController == null) {
+        if (Looper.myLooper() != Looper.getMainLooper() || mQuickScrubController == null
+                || mQuickScrubBlocked) {
             return;
         }
         mQuickScrubController.onQuickScrubProgress(progress);
@@ -809,6 +828,9 @@
     }
 
     private void switchToFinalAppAfterQuickScrub() {
+        if (mQuickScrubBlocked) {
+            return;
+        }
         mQuickScrubController.onQuickScrubEnd();
 
         // Normally this is handled in reset(), but since we are still scrubbing after the
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index f3a0e4f..d5c43a0 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -22,7 +22,6 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
 
 public class ClearAllButton extends Button {
@@ -37,14 +36,6 @@
     }
 
     @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        // Should be visible to accessibility even when completely covered by the task.
-        // Otherwise, we won't be able to scroll to it.
-        info.setVisibleToUser(true);
-    }
-
-    @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
         final boolean res = super.performAccessibilityAction(action, arguments);
         if (action == ACTION_ACCESSIBILITY_FOCUS) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index aeff476..578f36c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -53,7 +53,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
-import android.view.accessibility.AccessibilityEvent;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
@@ -1264,10 +1263,6 @@
     @Override
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
-        View currChild = getChildAt(mCurrentPage);
-        if (currChild != null) {
-            currChild.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-        }
         loadVisibleTaskData();
     }
 
@@ -1303,12 +1298,26 @@
 
     private void onChildViewsChanged() {
         final int childCount = getChildCount();
-        mClearAllButton.setAccessibilityTraversalAfter(
-                childCount == 0 ? NO_ID : getChildAt(childCount - 1).getId());
         mClearAllButton.setVisibility(childCount == 0 ? INVISIBLE : VISIBLE);
     }
 
     public void revealClearAllButton() {
         scrollTo(mIsRtl ? 0 : computeMaxScrollX(), 0);
     }
+
+    @Override
+    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+        if (FLIP_RECENTS) {
+            for (int i = getChildCount() - 1; i >= 0; --i) {
+                outChildren.add(getChildAt(i));
+            }
+        } else {
+            super.addChildrenForAccessibility(outChildren);
+        }
+    }
+
+    @Override
+    protected boolean isPageOrderFlipped() {
+        return FLIP_RECENTS;
+    }
 }
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index b112a8c..c75509e 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -101,10 +101,12 @@
     }
 
     public final void close(boolean animate) {
-        animate &= !Utilities.isPowerSaverOn(getContext());
+        animate &= !Utilities.isPowerSaverPreventingAnimation(getContext());
+        if (mIsOpen) {
+            BaseActivity.fromContext(getContext()).getUserEventDispatcher()
+                    .resetElapsedContainerMillis("container closed");
+        }
         handleClose(animate);
-        BaseActivity.fromContext(getContext()).getUserEventDispatcher()
-                .resetElapsedContainerMillis("container closed");
         mIsOpen = false;
     }
 
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 7979082..6c2fd8e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -1980,7 +1980,7 @@
             // Animations are disabled in power save mode, causing the repeated animation to jump
             // spastically between beginning and end states. Since this looks bad, we don't repeat
             // the animation in power save mode.
-            if (!Utilities.isPowerSaverOn(getContext())) {
+            if (!Utilities.isPowerSaverPreventingAnimation(getContext())) {
                 va.setRepeatMode(ValueAnimator.REVERSE);
                 va.setRepeatCount(ValueAnimator.INFINITE);
             }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 32539cd..14390ec 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -50,6 +50,7 @@
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
@@ -243,6 +244,10 @@
 
     private RotationHelper mRotationHelper;
 
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mLogOnDelayedResume = this::logOnDelayedResume;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         if (DEBUG_STRICT_MODE) {
@@ -727,10 +732,11 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
-        mAppWidgetHost.setListenIfResumed(false);
-
         getUserEventDispatcher().logActionCommand(Action.Command.STOP,
                 mStateManager.getState().containerType, -1);
+
+        mAppWidgetHost.setListenIfResumed(false);
+
         NotificationListener.removeNotificationsChangedListener();
         getStateManager().moveToRestState();
 
@@ -751,13 +757,23 @@
         UiFactory.onStart(this);
     }
 
+    private void logOnDelayedResume() {
+        if (hasBeenResumed()) {
+            getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
+                    mStateManager.getState().containerType, -1);
+            getUserEventDispatcher().startSession();
+        }
+    }
+
     @Override
     protected void onResume() {
         TraceHelper.beginSection("ON_RESUME");
         super.onResume();
         TraceHelper.partitionSection("ON_RESUME", "superCall");
 
-        getUserEventDispatcher().resetElapsedSessionMillis();
+        mHandler.removeCallbacks(mLogOnDelayedResume);
+        Utilities.postAsyncCallback(mHandler, mLogOnDelayedResume);
+
         setOnResumeCallback(null);
         // Process any items that were added while Launcher was away.
         InstallShortcutReceiver.disableAndFlushInstallQueue(
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a98867d..efbd004 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1495,12 +1495,20 @@
         return Math.abs(delta) > 0;
     }
 
-    public void scrollLeft() {
-        if (getNextPage() > 0) snapToPage(getNextPage() - 1);
+    public boolean scrollLeft() {
+        if (getNextPage() > 0) {
+            snapToPage(getNextPage() - 1);
+            return true;
+        }
+        return false;
     }
 
-    public void scrollRight() {
-        if (getNextPage() < getChildCount() -1) snapToPage(getNextPage() + 1);
+    public boolean scrollRight() {
+        if (getNextPage() < getChildCount() - 1) {
+            snapToPage(getNextPage() + 1);
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -1510,17 +1518,24 @@
         return ScrollView.class.getName();
     }
 
+    protected boolean isPageOrderFlipped() {
+        return false;
+    }
+
     /* Accessibility */
     @SuppressWarnings("deprecation")
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
+        final boolean pagesFlipped = isPageOrderFlipped();
         info.setScrollable(getPageCount() > 1);
         if (getCurrentPage() < getPageCount() - 1) {
-            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
+                    : AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
         }
         if (getCurrentPage() > 0) {
-            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+                    : AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
         }
 
         // Accessibility-wise, PagedView doesn't support long click, so disabling it.
@@ -1549,19 +1564,19 @@
         if (super.performAccessibilityAction(action, arguments)) {
             return true;
         }
+        final boolean pagesFlipped = isPageOrderFlipped();
         switch (action) {
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                if (getCurrentPage() < getPageCount() - 1) {
-                    scrollRight();
+                if (pagesFlipped ? scrollLeft() : scrollRight()) {
                     return true;
                 }
             } break;
             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                if (getCurrentPage() > 0) {
-                    scrollLeft();
+                if (pagesFlipped ? scrollRight() : scrollLeft()) {
                     return true;
                 }
-            } break;
+            }
+            break;
         }
         return false;
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 006dc95..4bd9a9b 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -39,7 +39,6 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
-import android.support.v4.os.BuildCompat;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -83,7 +82,8 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
-    public static final boolean ATLEAST_P = BuildCompat.isAtLeastP();
+    public static final boolean ATLEAST_P =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
 
     public static final boolean ATLEAST_OREO_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
@@ -492,7 +492,11 @@
                 LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
 
-    public static boolean isPowerSaverOn(Context context) {
+    public static boolean isPowerSaverPreventingAnimation(Context context) {
+        if (ATLEAST_P) {
+            // Battery saver mode no longer prevents animations.
+            return false;
+        }
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         return powerManager.isPowerSaveMode();
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2df34d5..6631f77 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2961,25 +2961,29 @@
     }
 
     @Override
-    public void scrollLeft() {
+    public boolean scrollLeft() {
+        boolean result = false;
         if (!workspaceInModalState() && !mIsSwitchingState) {
-            super.scrollLeft();
+            result = super.scrollLeft();
         }
         Folder openFolder = Folder.getOpen(mLauncher);
         if (openFolder != null) {
             openFolder.completeDragExit();
         }
+        return result;
     }
 
     @Override
-    public void scrollRight() {
+    public boolean scrollRight() {
+        boolean result = false;
         if (!workspaceInModalState() && !mIsSwitchingState) {
-            super.scrollRight();
+            result = super.scrollRight();
         }
         Folder openFolder = Folder.getOpen(mLauncher);
         if (openFolder != null) {
             openFolder.completeDragExit();
         }
+        return result;
     }
 
     /**
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVP.java b/src/com/android/launcher3/compat/UserManagerCompatVP.java
index 2e8a8eb..fa3902b 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVP.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVP.java
@@ -15,41 +15,20 @@
  */
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.os.Build;
 import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
+@TargetApi(Build.VERSION_CODES.P)
 public class UserManagerCompatVP extends UserManagerCompatVNMr1 {
-    private static final String TAG = "UserManagerCompatVP";
-
-    private Method mRequestQuietModeEnabled;
 
     UserManagerCompatVP(Context context) {
         super(context);
-        // TODO: Replace it with proper API call once SDK is ready.
-        try {
-            mRequestQuietModeEnabled = UserManager.class.getDeclaredMethod(
-                    "requestQuietModeEnabled", boolean.class, UserHandle.class);
-        } catch (NoSuchMethodException e) {
-            Log.e(TAG, "requestQuietModeEnabled is not available", e);
-        }
     }
 
     @Override
     public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
-        if (mRequestQuietModeEnabled == null) {
-            return false;
-        }
-        try {
-            return (boolean)
-                    mRequestQuietModeEnabled.invoke(mUserManager, enableQuietMode, user);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            Log.e(TAG, "Failed to invoke mRequestQuietModeEnabled", e);
-        }
-        return false;
+        return mUserManager.requestQuietModeEnabled(enableQuietMode, user);
     }
 }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 850c948..1842e19 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -125,6 +125,7 @@
         return null;
     }
 
+    private boolean mSessionStarted;
     private long mElapsedContainerMillis;
     private long mElapsedSessionMillis;
     private long mActionDurationMillis;
@@ -216,9 +217,11 @@
 
     public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
         LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
-        if (command == Action.Command.STOP && mAppOrTaskLaunch) {
-            // Prevent double logging by skipping STOP when app or task has been launched.
-            return;
+        if (command == Action.Command.STOP) {
+            if (mAppOrTaskLaunch || !mSessionStarted) {
+                mSessionStarted = false;
+                return;
+            }
         }
 
         if (dstTarget != null) {
@@ -405,7 +408,8 @@
 
     }
 
-    public final void resetElapsedSessionMillis() {
+    public final void startSession() {
+        mSessionStarted = true;
         mElapsedSessionMillis = SystemClock.uptimeMillis();
         mElapsedContainerMillis = SystemClock.uptimeMillis();
     }