Updating the folder page indicator to be more like the
framework page indicator (used in quick settings)

The active page is indicated with the accent color. During scroll
the active indicator expands to 2 dots corresponding to the visible pages.

Change-Id: Iaf57836b642cf87e5eed98048ecca7dd8e7643a4
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/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..f8eff69 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);
 
@@ -1286,7 +1281,7 @@
     }
 
     private void showPageIndicatorAtCurrentScroll() {
-        mPageIndicator.setProgress((float) getScrollX() / computeMaxScrollX());
+        mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
     }
 
     @Override
@@ -4265,20 +4260,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/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) {