diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2016-05-19 01:40:40 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-05-19 01:40:40 +0000 |
commit | c8e0fced52803f27ecaff234b1b6950b7b9fb18e (patch) | |
tree | 391350ff19d812cc0f52c9d5324ec90c530352ea /src | |
parent | bdeaa0b079781a4c18ddc6f1e298d03a2374882d (diff) | |
parent | c64cfdd8fa18de45fc1646c8ef2449f39ef83022 (diff) | |
download | android_packages_apps_Trebuchet-c8e0fced52803f27ecaff234b1b6950b7b9fb18e.tar.gz android_packages_apps_Trebuchet-c8e0fced52803f27ecaff234b1b6950b7b9fb18e.tar.bz2 android_packages_apps_Trebuchet-c8e0fced52803f27ecaff234b1b6950b7b9fb18e.zip |
Updating the folder page indicator to be more like the framework page indicator (used in quick settings)
am: c64cfdd8fa
* commit 'c64cfdd8fa18de45fc1646c8ef2449f39ef83022':
Updating the folder page indicator to be more like the framework page indicator (used in quick settings)
Change-Id: Ibd2d33dd2162495f19fe50eb952c4cf0fc788004
Diffstat (limited to 'src')
8 files changed, 308 insertions, 384 deletions
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 02e894b7f..86a1a397c 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -255,14 +255,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // 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 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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 abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // 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 386e01634..f8eff6987 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -809,11 +809,6 @@ public class Workspace extends PagedView 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 @@ public class Workspace extends PagedView } private void showPageIndicatorAtCurrentScroll() { - mPageIndicator.setProgress((float) getScrollX() / computeMaxScrollX()); + mPageIndicator.setScroll(getScrollX(), computeMaxScrollX()); } @Override @@ -4265,20 +4260,6 @@ public class Workspace extends PagedView 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 1ebe8fdfb..6df296e7d 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; 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 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Thunk FolderPagedView mContent; @Thunk View mContentWrapper; public ExtendedEditText mFolderName; + private PageIndicatorDots mPageIndicator; private View mFooter; private int mFooterHeight; @@ -228,6 +230,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList 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 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList 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 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList 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 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList }); } 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 e1a143165..bb8ca160d 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -25,8 +25,6 @@ import android.view.LayoutInflater; 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.ItemInfo; 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.ShortcutInfo; 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 @@ public class FolderPagedView extends PagedView { 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 @@ public class FolderPagedView extends PagedView { 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 @@ public class FolderPagedView extends PagedView { 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 @@ public class FolderPagedView extends PagedView { } } + @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 @@ public class FolderPagedView extends PagedView { 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 @@ public class FolderPagedView extends PagedView { 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 class FolderPagedView extends PagedView { } } - 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 6348b123b..77c579c93 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicator.java +++ b/src/com/android/launcher3/pageindicators/PageIndicator.java @@ -6,26 +6,11 @@ import java.util.ArrayList; 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 5ed342695..000000000 --- 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 a488f028a..4a1238ddd 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 { + + 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 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; + + // This value approximately overshoots to 1.5 times the original size. + private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f; -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 RectF sTempRect = new RectF(); - private LayoutInflater mLayoutInflater; - private int[] mWindowRange = new int[2]; - private int mMaxWindowSize; + 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(); + } + }; - private ArrayList<PageIndicatorDot> mMarkers = new ArrayList<>(); - @ViewDebug.ExportedProperty(category = "launcher") - private int mActiveMarkerIndex; + /** + * 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,175 +116,229 @@ public class PageIndicatorDots extends LinearLayout implements PageIndicator { 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); + mIsRtl = Utilities.isRtl(getResources()); } - 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(); - } + @Override + public View getView() { + return this; + } - // 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); + @Override + 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; - // 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); - } + 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 { - marker.inactivate(true); + animateToPostion(mActivePage); } + } + } - 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(); - } + 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(); + } + } - if (!allowAnimations) { - enableLayoutTransitions(); + public void stopAllAnimations() { + if (mAnimator != null) { + mAnimator.removeAllListeners(); + mAnimator.cancel(); + mAnimator = null; } + mFinalPosition = mActivePage; + CURRENT_POSITION.set(this, mFinalPosition); + } - mWindowRange[0] = windowStart; - mWindowRange[1] = windowEnd; + /** + * 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(); } - @Override - public void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) { - index = Math.max(0, Math.min(index, mMarkers.size())); + 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); + } - PageIndicatorDot m = - (PageIndicatorDot) mLayoutInflater.inflate(R.layout.page_indicator_marker, - this, false); - m.setMarkerDrawables(marker.activeId, marker.inactiveId); + animSet.addListener(new AnimatorListenerAdapter() { - mMarkers.add(index, m); - offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations); + @Override + public void onAnimationEnd(Animator animation) { + mEntryAnimationRadiusFactors = null; + invalidateOutline(); + invalidate(); + } + }); + animSet.start(); } @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); - } + public void setActiveMarker(int activePage) { + mActivePage = activePage; + invalidate(); } @Override - public void updateMarker(int index, PageMarkerResources marker) { - PageIndicatorDot m = mMarkers.get(index); - m.setMarkerDrawables(marker.activeId, marker.inactiveId); + public void addMarker() { + mNumPages++; + requestLayout(); } @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); - } + public void removeMarker() { + mNumPages--; + requestLayout(); } @Override - public View getView() { - return this; + public void setMarkersCount(int numMarkers) { + mNumPages = numMarkers; + requestLayout(); } @Override - public void setProgress(float progress) { + 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 - public void removeAllMarkers(boolean allowAnimations) { - while (mMarkers.size() > 0) { - removeMarker(Integer.MAX_VALUE, allowAnimations); + 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); } } - @Override - public void setActiveMarker(int index) { - // Center the active marker - mActiveMarkerIndex = index; - offsetWindowCenterTo(index, false); - } - - 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); + 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; + } + + if (mIsRtl) { + float rectWidth = sTempRect.width(); + sTempRect.right = getWidth() - sTempRect.left; + sTempRect.left = sTempRect.right - rectWidth; } - 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); + 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 + ); + } } - System.out.println("\tactive: " + mActiveMarkerIndex); } } diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java index 449bf0609..e48168616 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java @@ -20,8 +20,6 @@ import android.view.ViewConfiguration; 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 @@ public class PageIndicatorLine extends View implements PageIndicator { 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 @@ public class PageIndicatorLine extends View implements PageIndicator { } @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 @@ public class PageIndicatorLine extends View implements PageIndicator { } @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 @@ public class PageIndicatorLine extends View implements PageIndicator { } 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) { |