Merge "Defer starting the recents animation when swiping over back button" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 2f0cd78..59b637e 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
 import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -530,7 +531,7 @@
 
                 TransactionCompat t = new TransactionCompat();
                 for (RemoteAnimationTargetCompat target : targets) {
-                    if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+                    if (target.mode == MODE_OPENING) {
                         t.setAlpha(target.leash, alpha);
 
                         // TODO: This isn't correct at the beginning of the animation, but better
@@ -573,6 +574,16 @@
         }
     }
 
+    private boolean isLauncherInSetOfOpeningTargets(RemoteAnimationTargetCompat[] targets) {
+        int launcherTaskId = mLauncher.getTaskId();
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == MODE_OPENING && target.taskId == launcherTaskId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @return Runner that plays when user goes to Launcher
      *         ie. pressing home, swiping up from nav bar.
@@ -584,9 +595,12 @@
                                          Runnable finishedCallback) {
                 Handler handler = mLauncher.getWindow().getDecorView().getHandler();
                 postAtFrontOfQueueAsynchronously(handler, () -> {
-                    if (Utilities.getPrefs(mLauncher).getBoolean("pref_use_screenshot_animation",
-                            true) && mLauncher.isInState(LauncherState.OVERVIEW)) {
-                        // We use a separate transition for Overview mode.
+                    if ((Utilities.getPrefs(mLauncher).getBoolean("pref_use_screenshot_animation",
+                            true) && mLauncher.isInState(LauncherState.OVERVIEW))
+                            || !isLauncherInSetOfOpeningTargets(targets)) {
+                        // We use a separate transition for Overview mode. And we can skip the
+                        // animation in cases where Launcher is not in the set of opening targets.
+                        // This can happen when Launcher is already visible. ie. Closing a dialog.
                         setCurrentAnimator(null);
                         finishedCallback.run();
                         return;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 9a5640b..57e588b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -139,7 +139,9 @@
         applyProgress();
         if (mIsRecentsScrollingToFirstTask) {
             int scrollForFirstTask = mRecentsView.getScrollForPage(mRecentsView.getFirstTaskIndex());
-            mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask));
+            int scrollForPage0 = mRecentsView.getScrollForPage(0);
+            mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask
+                    + (1 - mTransitionProgress.value) * scrollForPage0));
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1576c4e..35c982c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,7 +21,6 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
-
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
 
@@ -42,13 +41,15 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.model.ModelPreload;
 import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.model.ModelPreload;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -225,9 +226,6 @@
         }
 
         View target = launcher.getDragLayer();
-        if (!target.getWindowId().isFocused()) {
-            return mNoOpTouchConsumer;
-        }
         return new LauncherTouchConsumer(launcher, target);
     }
 
@@ -253,6 +251,9 @@
 
         @Override
         public void accept(MotionEvent ev) {
+            if (!mTarget.getWindowId().isFocused()) {
+                return;
+            }
             int action = ev.getActionMasked();
             if (action == ACTION_DOWN) {
                 mTrackingStarted = false;
@@ -303,6 +304,12 @@
         public void updateTouchTracking(int interactionType) {
             mMainThreadExecutor.execute(() -> {
                 if (TouchConsumer.isInteractionQuick(interactionType)) {
+                    if (mLauncher.getWorkspace().runOnOverlayHidden(
+                            () -> updateTouchTracking(interactionType))) {
+                        // Hide the minus one overlay so launcher can get window focus.
+                        mLauncher.onQuickstepGestureStarted(true);
+                        return;
+                    }
                     Runnable onComplete = null;
                     if (interactionType == INTERACTION_QUICK_SCRUB) {
                         mQuickScrubController.onQuickScrubStart(true);
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index 54a9b88..2accd2d 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -14,21 +14,23 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.launcher3.allapps.InterceptingViewPager
+<com.android.launcher3.allapps.AllAppsPagedView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/all_apps_tabs_view_pager"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_below="@id/search_container_all_apps"
     android:layout_gravity="center_horizontal|top"
     android:layout_marginTop="@dimen/all_apps_header_tab_height"
-    android:clipChildren="false"
+    android:clipChildren="true"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
-    android:paddingTop="@dimen/all_apps_header_top_padding">
+    android:paddingTop="@dimen/all_apps_header_top_padding"
+    launcher:pageIndicator="@+id/tabs" >
 
     <include layout="@layout/all_apps_rv_layout" />
 
     <include layout="@layout/all_apps_rv_layout" />
 
-</com.android.launcher3.allapps.InterceptingViewPager>
\ No newline at end of file
+</com.android.launcher3.allapps.AllAppsPagedView>
\ No newline at end of file
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4d820bc..3fde58b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1975,18 +1975,12 @@
             return mAppLaunchSuccess;
         }
 
-        boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
-                && (item instanceof ShortcutInfo)
-                && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
-                && !((ShortcutInfo) item).isPromise();
-
         // Only launch using the new animation if the shortcut has not opted out (this is a
         // private contract between launcher and may be ignored in the future).
         boolean useLaunchAnimation = (v != null) &&
                 !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
         Bundle optsBundle = useLaunchAnimation
-                ? getActivityLaunchOptions(v, isShortcut || isInMultiWindowModeCompat())
+                ? getActivityLaunchOptions(v, isInMultiWindowModeCompat())
                 : null;
 
         UserHandle user = item == null ? null : item.user;
@@ -1997,6 +1991,11 @@
             intent.setSourceBounds(getViewBounds(v));
         }
         try {
+            boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
+                    && (item instanceof ShortcutInfo)
+                    && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                    && !((ShortcutInfo) item).isPromise();
             if (isShortcut) {
                 // Shortcuts need some special checks due to legacy reasons.
                 startShortcutIntentSafely(intent, optsBundle, item);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index bb137b0..ccb0a95 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -59,7 +59,7 @@
  * An abstraction of the original Workspace which supports browsing through a
  * sequential list of "pages"
  */
-public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+public abstract class PagedView<T extends View & PageIndicator> extends ViewGroup {
     private static final String TAG = "PagedView";
     private static final boolean DEBUG = false;
     protected static final int INVALID_PAGE = -1;
@@ -154,7 +154,7 @@
 
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
-    protected PageIndicator mPageIndicator;
+    protected T mPageIndicator;
 
     // Reordering
     // We use the min scale to determine how much to expand the actually PagedView measured
@@ -224,7 +224,6 @@
         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
         mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
         mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
-        setOnHierarchyChangeListener(this);
         setWillNotDraw(false);
     }
 
@@ -235,9 +234,9 @@
 
     public void initParentViews(View parent) {
         if (mPageIndicatorViewId > -1) {
-            mPageIndicator = (PageIndicator) parent.findViewById(mPageIndicatorViewId);
+            mPageIndicator = parent.findViewById(mPageIndicatorViewId);
             mPageIndicator.setMarkersCount(getChildCount());
-            mPageIndicator.setContentDescription(getPageIndicatorDescription());
+            mPageIndicator.setPageDescription(getPageIndicatorDescription());
         }
     }
 
@@ -282,7 +281,7 @@
         }
     }
 
-    public PageIndicator getPageIndicator() {
+    public T getPageIndicator() {
         return mPageIndicator;
     }
 
@@ -384,7 +383,7 @@
     private void updatePageIndicator() {
         // Update the page indicator (when we aren't reordering)
         if (mPageIndicator != null) {
-            mPageIndicator.setContentDescription(getPageIndicatorDescription());
+            mPageIndicator.setPageDescription(getPageIndicatorDescription());
             if (!isReordering(false)) {
                 mPageIndicator.setActiveMarker(getNextPage());
             }
@@ -748,63 +747,24 @@
         requestLayout();
     }
 
-    @Override
-    public void onChildViewAdded(View parent, View child) {
-        // Update the page indicator, we don't update the page indicator as we
-        // add/remove pages
-        if (mPageIndicator != null && !isReordering(false)) {
-            mPageIndicator.addMarker();
+    private void dispatchPageCountChanged() {
+        if (mPageIndicator != null) {
+            mPageIndicator.setMarkersCount(getChildCount());
         }
-
         // This ensures that when children are added, they get the correct transforms / alphas
         // in accordance with any scroll effects.
         invalidate();
     }
 
     @Override
-    public void onChildViewRemoved(View parent, View child) {
+    public void onViewAdded(View child) {
+        dispatchPageCountChanged();
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
         mCurrentPage = validateNewPage(mCurrentPage);
-        invalidate();
-    }
-
-    private void removeMarkerForView() {
-        // Update the page indicator, we don't update the page indicator as we
-        // add/remove pages
-        if (mPageIndicator != null && !isReordering(false)) {
-            mPageIndicator.removeMarker();
-        }
-    }
-
-    @Override
-    public void removeView(View v) {
-        // XXX: We should find a better way to hook into this before the view
-        // gets removed form its parent...
-        removeMarkerForView();
-        super.removeView(v);
-    }
-    @Override
-    public void removeViewInLayout(View v) {
-        // XXX: We should find a better way to hook into this before the view
-        // gets removed form its parent...
-        removeMarkerForView();
-        super.removeViewInLayout(v);
-    }
-    @Override
-    public void removeViewAt(int index) {
-        // XXX: We should find a better way to hook into this before the view
-        // gets removed form its parent...
-        removeMarkerForView();
-        super.removeViewAt(index);
-    }
-    @Override
-    public void removeAllViewsInLayout() {
-        // Update the page indicator, we don't update the page indicator as we
-        // add/remove pages
-        if (mPageIndicator != null) {
-            mPageIndicator.setMarkersCount(0);
-        }
-
-        super.removeAllViewsInLayout();
+        dispatchPageCountChanged();
     }
 
     protected int getChildOffset(int index) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2d24028..a4ca68a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,6 +52,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.LauncherOverlay;
@@ -75,6 +76,7 @@
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
 import com.android.launcher3.uioverrides.UiFactory;
@@ -100,10 +102,9 @@
  * Each page contains a number of icons, folders or widgets the user can
  * interact with. A workspace is meant to be used with a fixed width only.
  */
-public class Workspace extends PagedView
+public class Workspace extends PagedView<WorkspacePageIndicator>
         implements DropTarget, DragSource, View.OnTouchListener,
-        DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
-        Insettable, LauncherStateManager.StateHandler {
+        DragController.DragListener, Insettable, LauncherStateManager.StateHandler {
     private static final String TAG = "Launcher.Workspace";
 
     /** The value that {@link #mTransitionProgress} must be greater than for
@@ -235,6 +236,7 @@
     boolean mStartedSendingScrollEvents;
     float mLastOverlayScroll = 0;
     boolean mOverlayShown = false;
+    private Runnable mOnOverlayHiddenCallback;
 
     private boolean mForceDrawAdjacentPages = false;
     private boolean mPageRearrangeEnabled = false;
@@ -273,9 +275,7 @@
 
         mWallpaperOffset = new WallpaperOffsetInterpolator(this);
 
-        setOnHierarchyChangeListener(this);
         setHapticFeedbackEnabled(false);
-
         initWorkspace();
 
         // Disable multitouch across the workspace/all apps/customize tray
@@ -463,7 +463,7 @@
     }
 
     @Override
-    public void onChildViewAdded(View parent, View child) {
+    public void onViewAdded(View child) {
         if (!(child instanceof CellLayout)) {
             throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
         }
@@ -471,7 +471,7 @@
         cl.setOnInterceptTouchListener(this);
         cl.setClickable(true);
         cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-        super.onChildViewAdded(parent, child);
+        super.onViewAdded(child);
     }
 
     boolean isTouchActive() {
@@ -1163,6 +1163,10 @@
             if (mOverlayShown) {
                 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
                         Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+                if (mOnOverlayHiddenCallback != null) {
+                    mOnOverlayHiddenCallback.run();
+                    mOnOverlayHiddenCallback = null;
+                }
             }
             mOverlayShown = false;
         }
@@ -1188,6 +1192,59 @@
         mLauncher.getDragLayer().setAlpha(alpha);
     }
 
+    /**
+     * Runs the given callback when the minus one overlay is hidden. Specifically, it is run
+     * when launcher's window has focus and the overlay is no longer being shown. If a callback
+     * is already present, the new callback will chain off it so both are run.
+     *
+     * @return Whether the callback was deferred.
+     */
+    public boolean runOnOverlayHidden(Runnable callback) {
+        View rootView = getRootView();
+        if (rootView.hasWindowFocus()) {
+            if (mOverlayShown) {
+                chainOverlayHiddenCallback(callback);
+                return true;
+            } else {
+                callback.run();
+                return false;
+            }
+        }
+        ViewTreeObserver observer = rootView.getViewTreeObserver();
+        if (observer != null && observer.isAlive()) {
+            observer.addOnWindowFocusChangeListener(
+                    new ViewTreeObserver.OnWindowFocusChangeListener() {
+                        @Override
+                        public void onWindowFocusChanged(boolean hasFocus) {
+                            if (hasFocus) {
+                                // Defer further if the minus one overlay is still showing.
+                                if (mOverlayShown) {
+                                    chainOverlayHiddenCallback(callback);
+                                } else {
+                                    callback.run();
+                                }
+                                observer.removeOnWindowFocusChangeListener(this);
+                            }
+                        }
+                    });
+            return true;
+        }
+        return false;
+    }
+
+    private void chainOverlayHiddenCallback(Runnable callback) {
+        if (mOnOverlayHiddenCallback == null) {
+            mOnOverlayHiddenCallback = callback;
+        } else {
+            // Chain the new callback onto the previous callback(s).
+            Runnable oldCallback = mOnOverlayHiddenCallback;
+            mOnOverlayHiddenCallback = () -> {
+                oldCallback.run();
+                callback.run();
+            };
+        }
+    }
+
     @Override
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d4277db..a7eae39 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -21,8 +21,6 @@
 import android.os.Process;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.text.Selection;
@@ -50,7 +48,6 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.SpringAnimationHandler;
 import com.android.launcher3.config.FeatureFlags;
@@ -59,12 +56,8 @@
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.views.BottomUserEducationView;
 
-import java.util.List;
-import java.util.Set;
-
 /**
  * The all apps view container.
  */
@@ -80,9 +73,8 @@
 
     private SearchUiManager mSearchUiManager;
     private View mSearchContainer;
-    private InterceptingViewPager mViewPager;
+    private AllAppsPagedView mViewPager;
     private FloatingHeaderView mHeader;
-    private TabsPagerAdapter mTabsPagerAdapter;
 
     private SpannableStringBuilder mSearchQueryBuilder = null;
 
@@ -184,7 +176,7 @@
     }
 
     public AllAppsRecyclerView getActiveRecyclerView() {
-        if (!mUsingTabs || mViewPager.getCurrentItem() == 0) {
+        if (!mUsingTabs || mViewPager.getNextPage() == 0) {
             return mAH[AdapterHolder.MAIN].recyclerView;
         } else {
             return mAH[AdapterHolder.WORK].recyclerView;
@@ -329,9 +321,8 @@
         if (mUsingTabs) {
             mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
             mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
-            setupWorkProfileTabs();
+            onTabChanged(mViewPager.getNextPage());
         } else {
-            mTabsPagerAdapter = null;
             mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
             mAH[AdapterHolder.WORK].recyclerView = null;
         }
@@ -355,50 +346,35 @@
         int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
         View newView = LayoutInflater.from(getContext()).inflate(layout, this, false);
         addView(newView, index);
-        mViewPager = showTabs ? (InterceptingViewPager) newView : null;
+        if (showTabs) {
+            mViewPager = (AllAppsPagedView) newView;
+            mViewPager.initParentViews(this);
+            mViewPager.getPageIndicator().setContainerView(this);
+        } else {
+            mViewPager = null;
+        }
     }
 
     public View getRecyclerViewContainer() {
         return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
     }
 
-    private void setupWorkProfileTabs() {
-        if (mTabsPagerAdapter != null) {
-            return;
+    public void onTabChanged(int pos) {
+        mHeader.setMainActive(pos == 0);
+        reset();
+        applyTouchDelegate();
+        if (mAH[pos].recyclerView != null) {
+            mAH[pos].recyclerView.bindFastScrollbar();
+
+            findViewById(R.id.tab_personal)
+                    .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
+            findViewById(R.id.tab_work)
+                    .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
+
         }
-        final PersonalWorkSlidingTabStrip tabs = findViewById(R.id.tabs);
-        mViewPager.setAdapter(mTabsPagerAdapter = new TabsPagerAdapter());
-        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
-
-            @Override
-            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-                tabs.updateIndicatorPosition(position, positionOffset);
-            }
-
-            @Override
-            public void onPageSelected(int pos) {
-                tabs.updateTabTextColor(pos);
-                mHeader.setMainActive(pos == 0);
-                reset();
-                applyTouchDelegate();
-                if (mAH[pos].recyclerView != null) {
-                    mAH[pos].recyclerView.bindFastScrollbar();
-                }
-                if (pos == AdapterHolder.WORK) {
-                    BottomUserEducationView.showIfNeeded(mLauncher);
-                }
-            }
-
-            @Override
-            public void onPageScrollStateChanged(int state) {
-            }
-        });
-        mAH[AdapterHolder.MAIN].recyclerView.bindFastScrollbar();
-
-        findViewById(R.id.tab_personal)
-                .setOnClickListener((View view) -> mViewPager.setCurrentItem(0));
-        findViewById(R.id.tab_work)
-                .setOnClickListener((View view) -> mViewPager.setCurrentItem(1));
+        if (pos == AdapterHolder.WORK) {
+            BottomUserEducationView.showIfNeeded(mLauncher);
+        }
     }
 
     public AlphabeticalAppsList getApps() {
@@ -519,37 +495,4 @@
                     && verticalFadingEdge);
         }
     }
-
-    private class TabsPagerAdapter extends PagerAdapter {
-        @Override
-        public int getCount() {
-            return 2;
-        }
-
-        @Override
-        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
-            return view == object;
-        }
-
-        @NonNull
-        @Override
-        public Object instantiateItem(@NonNull ViewGroup container, int position) {
-            if (position == 0) {
-                return mAH[AdapterHolder.MAIN].recyclerView;
-            } else {
-                return mAH[AdapterHolder.WORK].recyclerView;
-            }
-        }
-
-        @Nullable
-        @Override
-        public CharSequence getPageTitle(int position) {
-            if (position == 0) {
-                return getResources().getString(R.string.all_apps_personal_tab);
-            } else {
-                return getResources().getString(R.string.all_apps_work_tab);
-            }
-        }
-    }
-
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
new file mode 100644
index 0000000..86186fd
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+
+public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
+
+    public AllAppsPagedView(Context context) {
+        this(context, null);
+    }
+
+    public AllAppsPagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected String getCurrentPageDescription() {
+        return getResources().getString(
+                getNextPage() == 0 ? R.string.all_apps_personal_tab : R.string.all_apps_work_tab);
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mPageIndicator.setScroll(l, mMaxScrollX);
+    }
+}
diff --git a/src/com/android/launcher3/allapps/InterceptingViewPager.java b/src/com/android/launcher3/allapps/InterceptingViewPager.java
deleted file mode 100644
index 3524ca9..0000000
--- a/src/com/android/launcher3/allapps/InterceptingViewPager.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2017 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.allapps;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.touch.SwipeDetector;
-
-import static android.view.MotionEvent.INVALID_POINTER_ID;
-
-
-public class InterceptingViewPager extends ViewPager {
-
-
-    private final PointF mDownPos = new PointF();
-    private final PointF mLastPos = new PointF();
-    private final int mSlop;
-    private int mActivePointerId = INVALID_POINTER_ID;
-
-    public InterceptingViewPager(@NonNull Context context) {
-        super(context);
-        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-    }
-
-    public InterceptingViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        boolean result = super.onInterceptTouchEvent(ev);
-        if (!result) {
-            switch (ev.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    mActivePointerId = ev.getPointerId(0);
-                    mDownPos.set(ev.getX(), ev.getY());
-                    mLastPos.set(mDownPos);
-                    break;
-                case MotionEvent.ACTION_POINTER_UP:
-                    int ptrIdx = ev.getActionIndex();
-                    int ptrId = ev.getPointerId(ptrIdx);
-                    if (ptrId == mActivePointerId) {
-                        final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
-                        mDownPos.set(
-                                ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
-                                ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
-                        mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
-                        mActivePointerId = ev.getPointerId(newPointerIdx);
-                    }
-                    break;
-                case MotionEvent.ACTION_MOVE:
-                    int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                    if (pointerIndex == INVALID_POINTER_ID) {
-                        break;
-                    }
-                    float deltaX = ev.getX() - mDownPos.x;
-                    float deltaY = ev.getY() - mDownPos.y;
-                    if (Math.abs(deltaX) > mSlop && Math.abs(deltaX) > Math.abs(deltaY)) {
-                        return true;
-                    }
-                    mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-                    break;
-                default:
-                    break;
-            }
-        }
-        return result;
-    }
-
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index 05cd655..b42d4cd 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -28,13 +28,13 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.util.Themes;
 
 /**
  * Supports two indicator colors, dedicated for personal and work tabs.
  */
-public class PersonalWorkSlidingTabStrip extends LinearLayout {
+public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageIndicator {
     private static final int POSITION_PERSONAL = 0;
     private static final int POSITION_WORK = 1;
 
@@ -51,6 +51,9 @@
     private float mIndicatorOffset;
     private int mSelectedPosition = 0;
 
+    private AllAppsContainerView mContainerView;
+    private int mLastActivePage = 0;
+
     public PersonalWorkSlidingTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         setOrientation(HORIZONTAL);
@@ -71,13 +74,13 @@
         mSharedPreferences = Launcher.getLauncher(getContext()).getSharedPrefs();
     }
 
-    public void updateIndicatorPosition(int position, float positionOffset) {
+    private void updateIndicatorPosition(int position, float positionOffset) {
         mIndicatorPosition = position;
         mIndicatorOffset = positionOffset;
         updateIndicatorPosition();
     }
 
-    public void updateTabTextColor(int pos) {
+    private void updateTabTextColor(int pos) {
         mSelectedPosition = pos;
         for (int i = 0; i < getChildCount(); i++) {
             Button tab = (Button) getChildAt(i);
@@ -129,8 +132,6 @@
 
         float y = getHeight() - mDividerPaint.getStrokeWidth();
         canvas.drawLine(getPaddingLeft(), y, getWidth() - getPaddingRight(), y, mDividerPaint);
-
-        final float middleX = getWidth() / 2.0f;
         canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
             mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
     }
@@ -153,4 +154,35 @@
             v.setPressed(false);
         });
     }
+
+    @Override
+    public void setScroll(int currentScroll, int totalScroll) {
+        if (currentScroll == totalScroll) {
+            updateIndicatorPosition(1, 0);
+        } else if (totalScroll > 0) {
+            updateIndicatorPosition(0, ((float) currentScroll) / totalScroll);
+        }
+    }
+
+    @Override
+    public void setActiveMarker(int activePage) {
+        updateTabTextColor(activePage);
+        if (mContainerView != null && mLastActivePage != activePage) {
+            mContainerView.onTabChanged(activePage);
+        }
+        mLastActivePage = activePage;
+    }
+
+    public void setContainerView(AllAppsContainerView containerView) {
+        mContainerView = containerView;
+    }
+
+    @Override
+    public void setMarkersCount(int numMarkers) { }
+
+    @Override
+    public void setPageDescription(CharSequence description) {
+        // We don't want custom page description as the tab-bar already has two tabs with their
+        // own descriptions.
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index f4462aa..a468cb5 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -44,14 +44,14 @@
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Map;
 
-public class FolderPagedView extends PagedView {
+public class FolderPagedView extends PagedView<PageIndicatorDots> {
 
     private static final String TAG = "FolderPagedView";
 
@@ -89,8 +89,6 @@
     private Folder mFolder;
     private PagedFolderKeyEventListener mKeyListener;
 
-    private PageIndicator mPageIndicator;
-
     public FolderPagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
         InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 5e3d216..3ce7291 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,41 +15,16 @@
  */
 package com.android.launcher3.pageindicators;
 
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
 /**
  * Base class for a page indicator.
  */
-public abstract class PageIndicator extends View {
+public interface PageIndicator {
 
-    protected int mNumPages = 1;
+    void setScroll(int currentScroll, int totalScroll);
 
-    public PageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
+    void setActiveMarker(int activePage);
 
-    public void setScroll(int currentScroll, int totalScroll) {}
+    void setMarkersCount(int numMarkers);
 
-    public void setActiveMarker(int activePage) {}
-
-    public void addMarker() {
-        mNumPages++;
-        onPageCountChanged();
-    }
-
-    public void removeMarker() {
-        mNumPages--;
-        onPageCountChanged();
-    }
-
-    public void setMarkersCount(int numMarkers) {
-        mNumPages = numMarkers;
-        onPageCountChanged();
-    }
-
-    protected void onPageCountChanged() {}
-
-    public void setShouldAutoHide(boolean shouldAutoHide) {}
+    void setPageDescription(CharSequence description);
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 6276c80..524ec3c 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -43,7 +43,7 @@
  * {@link PageIndicator} which shows dots per page. The active page is shown with the current
  * accent color.
  */
-public class PageIndicatorDots extends PageIndicator {
+public class PageIndicatorDots extends View implements PageIndicator {
 
     private static final float SHIFT_PER_ANIMATION = 0.5f;
     private static final float SHIFT_THRESHOLD = 0.1f;
@@ -79,6 +79,7 @@
     private final int mInActiveColor;
     private final boolean mIsRtl;
 
+    private int mNumPages;
     private int mActivePage;
 
     /**
@@ -221,11 +222,17 @@
     }
 
     @Override
-    protected void onPageCountChanged() {
+    public void setMarkersCount(int numMarkers) {
+        mNumPages = numMarkers;
         requestLayout();
     }
 
     @Override
+    public void setPageDescription(CharSequence description) {
+        setContentDescription(description);
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Add extra spacing of mDotRadius on all sides so than entry animation could be run.
         int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index ee4e4ee..4fc7d8a 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -41,7 +41,8 @@
  *
  * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
  */
-public class WorkspacePageIndicator extends PageIndicator implements Insettable, OnClickListener {
+public class WorkspacePageIndicator extends View
+        implements Insettable, OnClickListener, PageIndicator {
 
     private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
     private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
@@ -185,14 +186,18 @@
     }
 
     @Override
-    public void setActiveMarker(int activePage) {
+    public void setActiveMarker(int activePage) { }
+
+    @Override
+    public void setMarkersCount(int numMarkers) {
+        if (Float.compare(numMarkers, mNumPagesFloat) != 0) {
+            animateToNumPages(numMarkers);
+        }
     }
 
     @Override
-    protected void onPageCountChanged() {
-        if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
-            animateToNumPages(mNumPages);
-        }
+    public void setPageDescription(CharSequence description) {
+        setContentDescription(description);
     }
 
     public void setShouldAutoHide(boolean shouldAutoHide) {