Merge "Preventing unnecessary options update to QSB provider by checking if the options have changed before calling update" into ub-launcher3-calgary
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
deleted file mode 100644
index 6e3f5af..0000000
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
deleted file mode 100644
index 6dbc4f9..0000000
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current_folder.png b/res/drawable-hdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index c6c4228..0000000
--- a/res/drawable-hdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
deleted file mode 100644
index d9939b4..0000000
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
deleted file mode 100644
index 832f8ef..0000000
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current_folder.png b/res/drawable-mdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index b6c4d7f..0000000
--- a/res/drawable-mdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
deleted file mode 100644
index 7e18c05..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
deleted file mode 100644
index 866725f..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current_folder.png b/res/drawable-xhdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index ec19b7c..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
deleted file mode 100644
index d790e86..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
deleted file mode 100644
index 9550c61..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index 987c882..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
deleted file mode 100644
index 5655159..0000000
--- a/res/layout/page_indicator.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<com.android.launcher3.pageindicators.PageIndicatorDots
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:animateLayoutChanges="true"
-    launcher:windowSize="@integer/config_maxNumberOfPageIndicatorsToShow">
-</com.android.launcher3.pageindicators.PageIndicatorDots>
diff --git a/res/layout/page_indicator_marker.xml b/res/layout/page_indicator_marker.xml
deleted file mode 100644
index 357a761..0000000
--- a/res/layout/page_indicator_marker.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<com.android.launcher3.pageindicators.PageIndicatorDot
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:layout_width="12dp"
-    android:layout_height="12dp"
-    android:layout_gravity="center_vertical">
-    <ImageView
-        android:id="@+id/inactive"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:src="@drawable/ic_pageindicator_default"
-        />
-    <ImageView
-        android:id="@+id/active"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:src="@drawable/ic_pageindicator_current"
-        android:alpha="0"
-        android:scaleX="0.5"
-        android:scaleY="0.5"
-        />
-</com.android.launcher3.pageindicators.PageIndicatorDot>
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 87a4214..8a1b7d0 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -72,12 +72,13 @@
             android:textColorHint="#ff808080"
             android:textSize="14sp" />
 
-        <include
+        <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
-            android:layout_width="wrap_content"
-            android:layout_height="12dp"
             android:layout_gravity="center_vertical"
-            layout="@layout/page_indicator" />
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:elevation="1dp"
+            />
 
     </LinearLayout>
 
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index d445a7a..de1316e 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -73,12 +73,13 @@
             android:textColorHint="#ff808080"
             android:textSize="14sp" />
 
-        <include
+        <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
-            android:layout_width="wrap_content"
-            android:layout_height="12dp"
             android:layout_gravity="center_vertical"
-            layout="@layout/page_indicator" />
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:elevation="1dp"
+            />
 
     </LinearLayout>
 
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 2eb9b91..ca28ad3 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -31,11 +31,6 @@
         <attr name="customShadows" format="boolean" />
     </declare-styleable>
 
-    <!-- Page Indicator specific attributes. -->
-    <declare-styleable name="PageIndicatorDots">
-        <attr name="windowSize" format="integer"  />
-    </declare-styleable>
-
     <!-- PagedView specific attributes. These attributes are used to customize
          a PagedView view in XML files. -->
     <declare-styleable name="PagedView">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index dc3d4fa..754dc0c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -31,6 +31,7 @@
 
     <color name="workspace_edge_effect_color">#FFFFFFFF</color>
     <color name="folder_edge_effect_color">#FF757575</color>
+    <color name="page_indicator_dot_color">#FFDDDDDD</color>
 
     <color name="quantum_panel_text_color">#FF666666</color>
     <color name="quantum_panel_bg_color">#FFF5F5F5</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 281de08..6fac31e1 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -149,6 +149,7 @@
 <!-- Folders -->
     <!-- The size of the padding on the preview background drawable -->
     <dimen name="folder_preview_padding">6dp</dimen>
+    <dimen name="page_indicator_dot_size">8dp</dimen>
 
 <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 123f657..f487b12 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -505,7 +505,9 @@
         if (mExtractedColors != null && Utilities.isNycOrAbove()) {
             mExtractedColors.load(this);
             mHotseat.updateColor(mExtractedColors, !mPaused);
-            mPageIndicator.updateColor(mExtractedColors);
+            if (mPageIndicator != null) {
+                mPageIndicator.updateColor(mExtractedColors);
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 02e894b..86a1a39 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -255,14 +255,7 @@
         ViewGroup grandParent = (ViewGroup) parent.getParent();
         if (mPageIndicator == null && mPageIndicatorViewId > -1) {
             mPageIndicator = (PageIndicator) grandParent.findViewById(mPageIndicatorViewId);
-            mPageIndicator.removeAllMarkers(true);
-
-            ArrayList<PageIndicator.PageMarkerResources> markers = new ArrayList<>();
-            for (int i = 0; i < getChildCount(); ++i) {
-                markers.add(getPageIndicatorMarker(i));
-            }
-
-            mPageIndicator.addMarkers(markers, true);
+            mPageIndicator.setMarkersCount(getChildCount());
 
             OnClickListener listener = getPageIndicatorClickListener();
             if (listener != null) {
@@ -356,10 +349,6 @@
     PageIndicator getPageIndicator() {
         return mPageIndicator;
     }
-    protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
-        return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
-                R.drawable.ic_pageindicator_default);
-    }
 
     /**
      * Returns the index of the currently displayed page. When in free scroll mode, this is the page
@@ -962,10 +951,7 @@
         // Update the page indicator, we don't update the page indicator as we
         // add/remove pages
         if (mPageIndicator != null && !isReordering(false)) {
-            int pageIndex = indexOfChild(child);
-            mPageIndicator.addMarker(pageIndex,
-                    getPageIndicatorMarker(pageIndex),
-                    true);
+            mPageIndicator.addMarker();
         }
 
         // This ensures that when children are added, they get the correct transforms / alphas
@@ -982,11 +968,11 @@
         invalidate();
     }
 
-    private void removeMarkerForView(int index) {
+    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(index, true);
+            mPageIndicator.removeMarker();
         }
     }
 
@@ -994,21 +980,21 @@
     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(indexOfChild(v));
+        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(indexOfChild(v));
+        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(index);
+        removeMarkerForView();
         super.removeViewAt(index);
     }
     @Override
@@ -1016,7 +1002,7 @@
         // Update the page indicator, we don't update the page indicator as we
         // add/remove pages
         if (mPageIndicator != null) {
-            mPageIndicator.removeAllMarkers(true);
+            mPageIndicator.setMarkersCount(0);
         }
 
         super.removeAllViewsInLayout();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 386e016..0df657d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -809,11 +809,6 @@
         mWorkspaceScreens.put(newId, cl);
         mScreenOrder.add(newId);
 
-        // Update the page indicator marker
-        if (getPageIndicator() != null) {
-            getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
-        }
-
         // Update the model for the new screen
         mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
@@ -1280,13 +1275,15 @@
         // Update the page indicator progress.
         boolean isTransitioning = mIsSwitchingState
                 || (getLayoutTransition() != null && getLayoutTransition().isRunning());
-        if (mPageIndicator != null && !isTransitioning) {
+        if (!isTransitioning) {
             showPageIndicatorAtCurrentScroll();
         }
     }
 
     private void showPageIndicatorAtCurrentScroll() {
-        mPageIndicator.setProgress((float) getScrollX() / computeMaxScrollX());
+        if (mPageIndicator != null) {
+            mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
+        }
     }
 
     @Override
@@ -1342,7 +1339,9 @@
         // different effects based on device performance. On at least one relatively high-end
         // device I've tried, translating the launcher causes things to get quite laggy.
         setTranslationAndAlpha(mLauncher.getSearchDropTargetBar(), transX, alpha);
-        setTranslationAndAlpha(getPageIndicator().getView(), transX, alpha);
+        if (getPageIndicator() != null) {
+            setTranslationAndAlpha(getPageIndicator().getView(), transX, alpha);
+        }
         setTranslationAndAlpha(getChildAt(getCurrentPage()), transX, alpha);
         setTranslationAndAlpha(mLauncher.getHotseat(), transX, alpha);
 
@@ -4265,20 +4264,6 @@
         exitWidgetResizeMode();
     }
 
-    @Override
-    protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
-        long screenId = getScreenIdForPageIndex(pageIndex);
-        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
-            int count = mScreenOrder.size() - numCustomPages();
-            if (count > 1) {
-                return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
-                        R.drawable.ic_pageindicator_add);
-            }
-        }
-
-        return super.getPageIndicatorMarker(pageIndex);
-    }
-
     protected String getPageIndicatorDescription() {
         String settings = getResources().getString(R.string.settings_button_text);
         return getCurrentPageDescription() + ", " + settings;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 60070a8..38a578c 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -357,7 +357,8 @@
 
         final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
         final View hotseat = mLauncher.getHotseat();
-        final View pageIndicator = mWorkspace.getPageIndicator().getView();
+        final View pageIndicator = mWorkspace.getPageIndicator() == null ? null
+                : mWorkspace.getPageIndicator().getView();
         if (animated) {
             LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mWorkspace);
             scale.scaleX(mNewScale)
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 1ebe8fd..6df296e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -78,6 +78,7 @@
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Thunk;
@@ -148,6 +149,7 @@
     @Thunk FolderPagedView mContent;
     @Thunk View mContentWrapper;
     public ExtendedEditText mFolderName;
+    private PageIndicatorDots mPageIndicator;
 
     private View mFooter;
     private int mFooterHeight;
@@ -228,6 +230,7 @@
         mContent = (FolderPagedView) findViewById(R.id.folder_content);
         mContent.setFolder(this);
 
+        mPageIndicator = (PageIndicatorDots) findViewById(R.id.folder_page_indicator);
         mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
         mFolderName.setOnBackKeyListener(new ExtendedEditText.OnBackKeyListener() {
             @Override
@@ -612,7 +615,7 @@
             float textWidth =  mFolderName.getPaint().measureText(mFolderName.getText().toString());
             float translation = (footerWidth - textWidth) / 2;
             mFolderName.setTranslationX(mContent.mIsRtl ? -translation : translation);
-            mContent.setMarkerScale(0);
+            mPageIndicator.prepareEntryAnimation();
 
             // Do not update the flag if we are in drag mode. The flag will be updated, when we
             // actually drop the icon.
@@ -628,7 +631,7 @@
                                 AnimationUtils.loadInterpolator(mLauncher,
                                         android.R.interpolator.fast_out_slow_in)
                                 : new LogDecelerateInterpolator(100, 0));
-                    mContent.animateMarkers();
+                    mPageIndicator.playEntryAnimation();
 
                     if (updateAnimationFlag) {
                         mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
@@ -637,9 +640,9 @@
             });
         } else {
             mFolderName.setTranslationX(0);
-            mContent.setMarkerScale(1);
         }
 
+        mPageIndicator.stopAllAnimations();
         openFolderAnim.start();
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index e1a1431..bb8ca16 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -25,8 +25,6 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
@@ -39,8 +37,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.pageindicators.PageIndicator.PageMarkerResources;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
@@ -48,6 +44,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -65,13 +62,6 @@
     private static final int START_VIEW_REORDER_DELAY = 30;
     private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
 
-    private static final int PAGE_INDICATOR_ANIMATION_START_DELAY = 300;
-    private static final int PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY = 150;
-    private static final int PAGE_INDICATOR_ANIMATION_DURATION = 400;
-
-    // This value approximately overshoots to 1.5 times the original size.
-    private static final float PAGE_INDICATOR_OVERSHOOT_TENSION = 4.9f;
-
     /**
      * Fraction of the width to scroll when showing the next page hint.
      */
@@ -103,7 +93,7 @@
     private FocusIndicatorView mFocusIndicatorView;
     private PagedFolderKeyEventListener mKeyListener;
 
-    private PageIndicatorDots mPageIndicator;
+    private PageIndicator mPageIndicator;
 
     public FolderPagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -128,7 +118,7 @@
         mFolder = folder;
         mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
         mKeyListener = new PagedFolderKeyEventListener(folder);
-        mPageIndicator = (PageIndicatorDots) folder.findViewById(R.id.folder_page_indicator);
+        mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
     }
 
     /**
@@ -285,6 +275,12 @@
         }
     }
 
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mPageIndicator.setScroll(l, mMaxScrollX);
+    }
+
     /**
      * Updates position and rank of all the children in the view.
      * It essentially removes all views from all the pages and then adds them again in appropriate
@@ -369,7 +365,7 @@
         setEnableOverscroll(getPageCount() > 1);
 
         // Update footer
-        mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE);
+        mPageIndicator.getView().setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE);
         // Set the gravity as LEFT or RIGHT instead of START, as START depends on the actual text.
         mFolder.mFolderName.setGravity(getPageCount() > 1 ?
                 (mIsRtl ? Gravity.RIGHT : Gravity.LEFT) : Gravity.CENTER_HORIZONTAL);
@@ -409,12 +405,6 @@
                 pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]);
     }
 
-    @Override
-    protected PageMarkerResources getPageIndicatorMarker(int pageIndex) {
-        return new PageMarkerResources(R.drawable.ic_pageindicator_current_folder,
-                R.drawable.ic_pageindicator_default_folder);
-    }
-
     public boolean isFull() {
         return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
     }
@@ -676,28 +666,6 @@
         }
     }
 
-    public void setMarkerScale(float scale) {
-        int count  = mPageIndicator.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View marker = mPageIndicator.getChildAt(i);
-            marker.animate().cancel();
-            marker.setScaleX(scale);
-            marker.setScaleY(scale);
-        }
-    }
-
-    public void animateMarkers() {
-        int count  = mPageIndicator.getChildCount();
-        Interpolator interpolator = new OvershootInterpolator(PAGE_INDICATOR_OVERSHOOT_TENSION);
-        for (int i = 0; i < count; i++) {
-            mPageIndicator.getChildAt(i).animate().scaleX(1).scaleY(1)
-                .setInterpolator(interpolator)
-                .setDuration(PAGE_INDICATOR_ANIMATION_DURATION)
-                .setStartDelay(PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY * i
-                        + PAGE_INDICATOR_ANIMATION_START_DELAY);
-        }
-    }
-
     public int itemsPerPage() {
         return mMaxItemsPerPage;
     }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 6348b12..77c579c 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -6,26 +6,11 @@
 
 public interface PageIndicator {
     View getView();
-    void setProgress(float progress);
+    void setScroll(int currentScroll, int totalScroll);
 
-    void removeAllMarkers(boolean allowAnimations);
-    void addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations);
     void setActiveMarker(int activePage);
-    void addMarker(int pageIndex, PageMarkerResources pageIndicatorMarker, boolean allowAnimations);
-    void removeMarker(int pageIndex, boolean allowAnimations);
-    void updateMarker(int pageIndex, PageMarkerResources pageIndicatorMarker);
 
-    /**
-     * Contains two resource ids for each page indicator marker (e.g. dots):
-     * one for when the page is active and one for when the page is inactive.
-     */
-    class PageMarkerResources {
-        int activeId;
-        int inactiveId;
-
-        public PageMarkerResources(int aId, int iaId) {
-            activeId = aId;
-            inactiveId = iaId;
-        }
-    }
+    void addMarker();
+    void removeMarker();
+    void setMarkersCount(int numMarkers);
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDot.java b/src/com/android/launcher3/pageindicators/PageIndicatorDot.java
deleted file mode 100644
index 5ed3426..0000000
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDot.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2011 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.pageindicators;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.launcher3.R;
-
-public class PageIndicatorDot extends FrameLayout {
-    @SuppressWarnings("unused")
-    private static final String TAG = "PageIndicator";
-
-    private static final int MARKER_FADE_DURATION = 175;
-
-    private ImageView mActiveMarker;
-    private ImageView mInactiveMarker;
-    private boolean mIsActive = false;
-
-    public PageIndicatorDot(Context context) {
-        this(context, null);
-    }
-
-    public PageIndicatorDot(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PageIndicatorDot(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mActiveMarker = (ImageView) findViewById(R.id.active);
-        mInactiveMarker = (ImageView) findViewById(R.id.inactive);
-    }
-
-    void setMarkerDrawables(int activeResId, int inactiveResId) {
-        Resources r = getResources();
-        mActiveMarker.setImageDrawable(r.getDrawable(activeResId));
-        mInactiveMarker.setImageDrawable(r.getDrawable(inactiveResId));
-    }
-
-    void activate(boolean immediate) {
-        if (immediate) {
-            mActiveMarker.animate().cancel();
-            mActiveMarker.setAlpha(1f);
-            mActiveMarker.setScaleX(1f);
-            mActiveMarker.setScaleY(1f);
-            mInactiveMarker.animate().cancel();
-            mInactiveMarker.setAlpha(0f);
-        } else {
-            mActiveMarker.animate()
-                    .alpha(1f)
-                    .scaleX(1f)
-                    .scaleY(1f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-            mInactiveMarker.animate()
-                    .alpha(0f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-        }
-        mIsActive = true;
-    }
-
-    void inactivate(boolean immediate) {
-        if (immediate) {
-            mInactiveMarker.animate().cancel();
-            mInactiveMarker.setAlpha(1f);
-            mActiveMarker.animate().cancel();
-            mActiveMarker.setAlpha(0f);
-            mActiveMarker.setScaleX(0.5f);
-            mActiveMarker.setScaleY(0.5f);
-        } else {
-            mInactiveMarker.animate().alpha(1f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-            mActiveMarker.animate()
-                    .alpha(0f)
-                    .scaleX(0.5f)
-                    .scaleY(0.5f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-        }
-        mIsActive = false;
-    }
-
-    boolean isActive() {
-        return mIsActive;
-    }
-}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index a488f02..4a1238d 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -16,32 +16,97 @@
 
 package com.android.launcher3.pageindicators;
 
-import android.animation.LayoutTransition;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
-import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.view.animation.Interpolator;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
+import android.util.Property;
 import android.view.View;
-import android.view.ViewDebug;
-import android.widget.LinearLayout;
+import android.view.ViewOutlineProvider;
+import android.view.animation.OvershootInterpolator;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 
-import java.util.ArrayList;
+/**
+ * {@link PageIndicator} which shows dots per page. The active page is shown with the current
+ * accent color.
+ */
+public class PageIndicatorDots extends View implements PageIndicator {
 
-public class PageIndicatorDots extends LinearLayout implements PageIndicator {
-    @SuppressWarnings("unused")
-    private static final String TAG = "PageIndicator";
-    // Want this to look good? Keep it odd
-    private static final boolean MODULATE_ALPHA_ENABLED = false;
+    private static final float SHIFT_PER_ANIMATION = 0.5f;
+    private static final float SHIFT_THRESHOLD = 0.1f;
+    private static final long ANIMATION_DURATION = 150;
 
-    private LayoutInflater mLayoutInflater;
-    private int[] mWindowRange = new int[2];
-    private int mMaxWindowSize;
+    private static final int ENTER_ANIMATION_START_DELAY = 300;
+    private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
+    private static final int ENTER_ANIMATION_DURATION = 400;
 
-    private ArrayList<PageIndicatorDot> mMarkers = new ArrayList<>();
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private int mActiveMarkerIndex;
+    // This value approximately overshoots to 1.5 times the original size.
+    private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
+
+    private static final RectF sTempRect = new RectF();
+
+    private static final Property<PageIndicatorDots, Float> CURRENT_POSITION
+            = new Property<PageIndicatorDots, Float>(float.class, "current_position") {
+        @Override
+        public Float get(PageIndicatorDots obj) {
+            return obj.mCurrentPosition;
+        }
+
+        @Override
+        public void set(PageIndicatorDots obj, Float pos) {
+            obj.mCurrentPosition = pos;
+            obj.invalidate();
+            obj.invalidateOutline();
+        }
+    };
+
+    /**
+     * Listener for keep running the animation until the final state is reached.
+     */
+    private final AnimatorListenerAdapter mAnimCycleListener = new AnimatorListenerAdapter() {
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mAnimator = null;
+            animateToPostion(mFinalPosition);
+        }
+    };
+
+    private final Paint mCirclePaint;
+    private final float mDotRadius;
+    private final int mActiveColor;
+    private final int mInActiveColor;
+    private final boolean mIsRtl;
+
+    private int mNumPages;
+    private int mActivePage;
+
+    /**
+     * The current position of the active dot including the animation progress.
+     * For ex:
+     *   0.0  => Active dot is at position 0
+     *   0.33 => Active dot is at position 0 and is moving towards 1
+     *   0.50 => Active dot is at position [0, 1]
+     *   0.77 => Active dot has left position 0 and is collapsing towards position 1
+     *   1.0  => Active dot is at position 1
+     */
+    private float mCurrentPosition;
+    private float mFinalPosition;
+    private ObjectAnimator mAnimator;
+
+    private float[] mEntryAnimationRadiusFactors;
 
     public PageIndicatorDots(Context context) {
         this(context, null);
@@ -51,137 +116,18 @@
         this(context, attrs, 0);
     }
 
-    public PageIndicatorDots(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.PageIndicatorDots, defStyle, 0);
-        mMaxWindowSize = a.getInteger(R.styleable.PageIndicatorDots_windowSize, 15);
-        mWindowRange[0] = 0;
-        mWindowRange[1] = 0;
-        mLayoutInflater = LayoutInflater.from(context);
-        a.recycle();
+    public PageIndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
 
-        // Set the layout transition properties
-        LayoutTransition transition = getLayoutTransition();
-        transition.setDuration(175);
-    }
+        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mCirclePaint.setStyle(Style.FILL);
+        mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
+        setOutlineProvider(new MyOutlineProver());
 
-    private void enableLayoutTransitions() {
-        LayoutTransition transition = getLayoutTransition();
-        transition.enableTransitionType(LayoutTransition.APPEARING);
-        transition.enableTransitionType(LayoutTransition.DISAPPEARING);
-        transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
-        transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-    }
+        mActiveColor = getResources().getColor(R.color.launcher_accent_color);
+        mInActiveColor = getResources().getColor(R.color.page_indicator_dot_color);
 
-    private void disableLayoutTransitions() {
-        LayoutTransition transition = getLayoutTransition();
-        transition.disableTransitionType(LayoutTransition.APPEARING);
-        transition.disableTransitionType(LayoutTransition.DISAPPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-    }
-
-    public void offsetWindowCenterTo(int activeIndex, boolean allowAnimations) {
-        if (activeIndex < 0) {
-            new Throwable().printStackTrace();
-        }
-        int windowSize = Math.min(mMarkers.size(), mMaxWindowSize);
-        int hWindowSize = (int) windowSize / 2;
-        float hfWindowSize = windowSize / 2f;
-        int windowStart = Math.max(0, activeIndex - hWindowSize);
-        int windowEnd = Math.min(mMarkers.size(), windowStart + mMaxWindowSize);
-        windowStart = windowEnd - Math.min(mMarkers.size(), windowSize);
-        int windowMid = windowStart + (windowEnd - windowStart) / 2;
-        boolean windowAtStart = (windowStart == 0);
-        boolean windowAtEnd = (windowEnd == mMarkers.size());
-        boolean windowMoved = (mWindowRange[0] != windowStart) ||
-                (mWindowRange[1] != windowEnd);
-
-        if (!allowAnimations) {
-            disableLayoutTransitions();
-        }
-
-        // Remove all the previous children that are no longer in the window
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            PageIndicatorDot marker = (PageIndicatorDot) getChildAt(i);
-            int markerIndex = mMarkers.indexOf(marker);
-            if (markerIndex < windowStart || markerIndex >= windowEnd) {
-                removeView(marker);
-            }
-        }
-
-        // Add all the new children that belong in the window
-        for (int i = 0; i < mMarkers.size(); ++i) {
-            PageIndicatorDot marker = (PageIndicatorDot) mMarkers.get(i);
-            if (windowStart <= i && i < windowEnd) {
-                if (indexOfChild(marker) < 0) {
-                    addView(marker, i - windowStart);
-                }
-                if (i == activeIndex) {
-                    marker.activate(windowMoved);
-                } else {
-                    marker.inactivate(windowMoved);
-                }
-            } else {
-                marker.inactivate(true);
-            }
-
-            if (MODULATE_ALPHA_ENABLED) {
-                // Update the marker's alpha
-                float alpha = 1f;
-                if (mMarkers.size() > windowSize) {
-                    if ((windowAtStart && i > hWindowSize) ||
-                        (windowAtEnd && i < (mMarkers.size() - hWindowSize)) ||
-                        (!windowAtStart && !windowAtEnd)) {
-                        alpha = 1f - Math.abs((i - windowMid) / hfWindowSize);
-                    }
-                }
-                marker.animate().alpha(alpha).setDuration(500).start();
-            }
-        }
-
-        if (!allowAnimations) {
-            enableLayoutTransitions();
-        }
-
-        mWindowRange[0] = windowStart;
-        mWindowRange[1] = windowEnd;
-    }
-
-    @Override
-    public void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) {
-        index = Math.max(0, Math.min(index, mMarkers.size()));
-
-        PageIndicatorDot m =
-            (PageIndicatorDot) mLayoutInflater.inflate(R.layout.page_indicator_marker,
-                    this, false);
-        m.setMarkerDrawables(marker.activeId, marker.inactiveId);
-
-        mMarkers.add(index, m);
-        offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
-    }
-
-    @Override
-    public void addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations) {
-        for (int i = 0; i < markers.size(); ++i) {
-            addMarker(Integer.MAX_VALUE, markers.get(i), allowAnimations);
-        }
-    }
-
-    @Override
-    public void updateMarker(int index, PageMarkerResources marker) {
-        PageIndicatorDot m = mMarkers.get(index);
-        m.setMarkerDrawables(marker.activeId, marker.inactiveId);
-    }
-
-    @Override
-    public void removeMarker(int index, boolean allowAnimations) {
-        if (mMarkers.size() > 0) {
-            index = Math.max(0, Math.min(mMarkers.size() - 1, index));
-            mMarkers.remove(index);
-            offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
-        }
+        mIsRtl = Utilities.isRtl(getResources());
     }
 
     @Override
@@ -190,36 +136,209 @@
     }
 
     @Override
-    public void setProgress(float progress) {
+    public void setScroll(int currentScroll, int totalScroll) {
+        if (mNumPages > 1) {
+            if (mIsRtl) {
+                currentScroll = totalScroll - currentScroll;
+            }
+            int scrollPerPage = totalScroll / (mNumPages - 1);
+            int absScroll = mActivePage * scrollPerPage;
+            float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
+
+            if ((absScroll - currentScroll) > scrollThreshold) {
+                // current scroll is before absolute scroll
+                animateToPostion(mActivePage - SHIFT_PER_ANIMATION);
+            } else if ((currentScroll - absScroll) > scrollThreshold) {
+                // current scroll is ahead of absolute scroll
+                animateToPostion(mActivePage + SHIFT_PER_ANIMATION);
+            } else {
+                animateToPostion(mActivePage);
+            }
+        }
+    }
+
+    private void animateToPostion(float position) {
+        mFinalPosition = position;
+        if (Math.abs(mCurrentPosition - mFinalPosition) < SHIFT_THRESHOLD) {
+            mCurrentPosition = mFinalPosition;
+        }
+        if (mAnimator == null && Float.compare(mCurrentPosition, mFinalPosition) != 0) {
+            float positionForThisAnim = mCurrentPosition > mFinalPosition ?
+                    mCurrentPosition - SHIFT_PER_ANIMATION : mCurrentPosition + SHIFT_PER_ANIMATION;
+            mAnimator = ObjectAnimator.ofFloat(this, CURRENT_POSITION, positionForThisAnim);
+            mAnimator.addListener(mAnimCycleListener);
+            mAnimator.setDuration(ANIMATION_DURATION);
+            mAnimator.start();
+        }
+    }
+
+    public void stopAllAnimations() {
+        if (mAnimator != null) {
+            mAnimator.removeAllListeners();
+            mAnimator.cancel();
+            mAnimator = null;
+        }
+        mFinalPosition = mActivePage;
+        CURRENT_POSITION.set(this, mFinalPosition);
+    }
+
+    /**
+     * Sets up up the page indicator to play the entry animation.
+     * {@link #playEntryAnimation()} must be called after this.
+     */
+    public void prepareEntryAnimation() {
+        mEntryAnimationRadiusFactors = new float[mNumPages];
+        invalidate();
+    }
+
+    public void playEntryAnimation() {
+        int count  = mEntryAnimationRadiusFactors.length;
+        if (count == 0) {
+            mEntryAnimationRadiusFactors = null;
+            invalidate();
+            return;
+        }
+
+        Interpolator interpolator = new OvershootInterpolator(ENTER_ANIMATION_OVERSHOOT_TENSION);
+        AnimatorSet animSet = new AnimatorSet();
+        for (int i = 0; i < count; i++) {
+            ValueAnimator anim = ValueAnimator.ofFloat(0, 1).setDuration(ENTER_ANIMATION_DURATION);
+            final int index = i;
+            anim.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    mEntryAnimationRadiusFactors[index] = (Float) animation.getAnimatedValue();
+                    invalidate();
+                }
+            });
+            anim.setInterpolator(interpolator);
+            anim.setStartDelay(ENTER_ANIMATION_START_DELAY + ENTER_ANIMATION_STAGGERED_DELAY * i);
+            animSet.play(anim);
+        }
+
+        animSet.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mEntryAnimationRadiusFactors = null;
+                invalidateOutline();
+                invalidate();
+            }
+        });
+        animSet.start();
     }
 
     @Override
-    public void removeAllMarkers(boolean allowAnimations) {
-        while (mMarkers.size() > 0) {
-            removeMarker(Integer.MAX_VALUE, allowAnimations);
-        }
+    public void setActiveMarker(int activePage) {
+        mActivePage = activePage;
+        invalidate();
     }
 
     @Override
-    public void setActiveMarker(int index) {
-        // Center the active marker
-        mActiveMarkerIndex = index;
-        offsetWindowCenterTo(index, false);
+    public void addMarker() {
+        mNumPages++;
+        requestLayout();
     }
 
-    private void dumpState(String txt) {
-        System.out.println(txt);
-        System.out.println("\tmMarkers: " + mMarkers.size());
-        for (int i = 0; i < mMarkers.size(); ++i) {
-            PageIndicatorDot m = mMarkers.get(i);
-            System.out.println("\t\t(" + i + ") " + m);
+    @Override
+    public void removeMarker() {
+        mNumPages--;
+        requestLayout();
+    }
+
+    @Override
+    public void setMarkersCount(int numMarkers) {
+        mNumPages = numMarkers;
+        requestLayout();
+    }
+
+    @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 ?
+                MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
+        int height= MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ?
+                MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        // Draw all page indicators;
+        float circleGap = 3 * mDotRadius;
+        float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+
+        float x = startX + mDotRadius;
+        float y = canvas.getHeight() / 2;
+
+        if (mEntryAnimationRadiusFactors != null) {
+            // During entry animation, only draw the circles
+            if (mIsRtl) {
+                x = getWidth() - x;
+                circleGap = -circleGap;
+            }
+            for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
+                mCirclePaint.setColor(i == mActivePage ? mActiveColor : mInActiveColor);
+                canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], mCirclePaint);
+                x += circleGap;
+            }
+        } else {
+            mCirclePaint.setColor(mInActiveColor);
+            for (int i = 0; i < mNumPages; i++) {
+                canvas.drawCircle(x, y, mDotRadius, mCirclePaint);
+                x += circleGap;
+            }
+
+            mCirclePaint.setColor(mActiveColor);
+            canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mCirclePaint);
         }
-        System.out.println("\twindow: [" + mWindowRange[0] + ", " + mWindowRange[1] + "]");
-        System.out.println("\tchildren: " + getChildCount());
-        for (int i = 0; i < getChildCount(); ++i) {
-            PageIndicatorDot m = (PageIndicatorDot) getChildAt(i);
-            System.out.println("\t\t(" + i + ") " + m);
+    }
+
+    private RectF getActiveRect() {
+        float startCircle = (int) mCurrentPosition;
+        float delta = mCurrentPosition - startCircle;
+        float diameter = 2 * mDotRadius;
+        float circleGap = 3 * mDotRadius;
+        float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+
+        sTempRect.top = getHeight() * 0.5f - mDotRadius;
+        sTempRect.bottom = getHeight() * 0.5f + mDotRadius;
+        sTempRect.left = startX + startCircle * circleGap;
+        sTempRect.right = sTempRect.left + diameter;
+
+        if (delta < SHIFT_PER_ANIMATION) {
+            // dot is capturing the right circle.
+            sTempRect.right += delta * circleGap * 2;
+        } else {
+            // Dot is leaving the left circle.
+            sTempRect.right += circleGap;
+
+            delta -= SHIFT_PER_ANIMATION;
+            sTempRect.left += delta * circleGap * 2;
         }
-        System.out.println("\tactive: " + mActiveMarkerIndex);
+
+        if (mIsRtl) {
+            float rectWidth = sTempRect.width();
+            sTempRect.right = getWidth() - sTempRect.left;
+            sTempRect.left = sTempRect.right - rectWidth;
+        }
+        return sTempRect;
+    }
+
+    private class MyOutlineProver extends ViewOutlineProvider {
+
+        @Override
+        public void getOutline(View view, Outline outline) {
+            if (mEntryAnimationRadiusFactors == null) {
+                RectF activeRect = getActiveRect();
+                outline.setRoundRect(
+                        (int) activeRect.left,
+                        (int) activeRect.top,
+                        (int) activeRect.right,
+                        (int) activeRect.bottom,
+                        mDotRadius
+                );
+            }
+        }
     }
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
index 449bf06..e4816861 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
@@ -20,8 +20,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dynamicui.ExtractedColors;
 
-import java.util.ArrayList;
-
 /**
  * A PageIndicator that briefly shows a fraction of a line when moving between pages.
  *
@@ -43,17 +41,17 @@
     private int mNumPages = 1;
     private Paint mLinePaint;
 
-    private Property<Paint, Integer> mPaintAlphaProperty
-            = new Property<Paint, Integer>(Integer.class, "paint_alpha") {
+    private static final Property<PageIndicatorLine, Integer> PAINT_ALPHA
+            = new Property<PageIndicatorLine, Integer>(Integer.class, "paint_alpha") {
         @Override
-        public Integer get(Paint paint) {
-            return paint.getAlpha();
+        public Integer get(PageIndicatorLine obj) {
+            return obj.mLinePaint.getAlpha();
         }
 
         @Override
-        public void set(Paint paint, Integer alpha) {
-            paint.setAlpha(alpha);
-            invalidate();
+        public void set(PageIndicatorLine obj, Integer alpha) {
+            obj.mLinePaint.setAlpha(alpha);
+            obj.invalidate();
         }
     };
 
@@ -99,13 +97,12 @@
     }
 
     @Override
-    public void setProgress(float progress) {
+    public void setScroll(int currentScroll, int totalScroll) {
         if (getAlpha() == 0) {
             return;
         }
-        progress = Utilities.boundToRange(progress, 0f, 1f);
         animateLineToAlpha(mAlpha);
-        mProgress = progress;
+        mProgress = Utilities.boundToRange(((float) currentScroll) / totalScroll, 0f, 1f);;
         invalidate();
 
         // Hide after a brief period.
@@ -114,32 +111,22 @@
     }
 
     @Override
-    public void removeAllMarkers(boolean allowAnimations) {
-        mNumPages = 0;
-    }
-
-    @Override
-    public void addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations) {
-        mNumPages += markers.size();
-    }
-
-    @Override
     public void setActiveMarker(int activePage) {
     }
 
     @Override
-    public void addMarker(int pageIndex, PageMarkerResources pageIndicatorMarker,
-            boolean allowAnimations) {
+    public void addMarker() {
         mNumPages++;
     }
 
     @Override
-    public void removeMarker(int pageIndex, boolean allowAnimations) {
+    public void removeMarker() {
         mNumPages--;
     }
 
     @Override
-    public void updateMarker(int pageIndex, PageMarkerResources pageIndicatorMarker) {
+    public void setMarkersCount(int numMarkers) {
+        mNumPages = numMarkers;
     }
 
     /**
@@ -174,7 +161,7 @@
             }
             mLineAlphaAnimator.cancel();
         }
-        mLineAlphaAnimator = ObjectAnimator.ofInt(mLinePaint, mPaintAlphaProperty, alpha);
+        mLineAlphaAnimator = ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha);
         mLineAlphaAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {