1 package com.android.launcher3.pageindicators;
3 import android.animation.Animator;
4 import android.animation.AnimatorListenerAdapter;
5 import android.animation.ObjectAnimator;
6 import android.animation.ValueAnimator;
7 import android.content.Context;
8 import android.content.res.Resources;
9 import android.graphics.Canvas;
10 import android.graphics.Color;
11 import android.graphics.Paint;
12 import android.os.Handler;
13 import android.os.Looper;
14 import android.support.v4.graphics.ColorUtils;
15 import android.util.AttributeSet;
16 import android.util.Log;
17 import android.util.Property;
18 import android.view.ViewConfiguration;
19 import android.widget.ImageView;
21 import com.android.launcher3.Launcher;
22 import com.android.launcher3.R;
23 import com.android.launcher3.Utilities;
24 import com.android.launcher3.dynamicui.ExtractedColors;
27 * A PageIndicator that briefly shows a fraction of a line when moving between pages.
29 * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
31 public class PageIndicatorLineCaret extends PageIndicator {
32 private static final String TAG = "PageIndicatorLine";
34 private static final int[] sTempCoords = new int[2];
36 private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
37 private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
38 public static final int WHITE_ALPHA = (int) (0.70f * 255);
39 public static final int BLACK_ALPHA = (int) (0.65f * 255);
41 private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
42 private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
43 private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
45 private ValueAnimator[] mAnimators = new ValueAnimator[3];
47 private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
49 private boolean mShouldAutoHide = true;
51 // The alpha of the line when it is showing.
52 private int mActiveAlpha = 0;
53 // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
55 // A float value representing the number of pages, to allow for an animation when it changes.
56 private float mNumPagesFloat;
57 private int mCurrentScroll;
58 private int mTotalScroll;
59 private Paint mLinePaint;
60 private Launcher mLauncher;
61 private final int mLineHeight;
62 private ImageView mAllAppsHandle;
64 private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA
65 = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") {
67 public Integer get(PageIndicatorLineCaret obj) {
68 return obj.mLinePaint.getAlpha();
72 public void set(PageIndicatorLineCaret obj, Integer alpha) {
73 obj.mLinePaint.setAlpha(alpha);
78 private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES
79 = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") {
81 public Float get(PageIndicatorLineCaret obj) {
82 return obj.mNumPagesFloat;
86 public void set(PageIndicatorLineCaret obj, Float numPages) {
87 obj.mNumPagesFloat = numPages;
92 private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL
93 = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") {
95 public Integer get(PageIndicatorLineCaret obj) {
96 return obj.mTotalScroll;
100 public void set(PageIndicatorLineCaret obj, Integer totalScroll) {
101 obj.mTotalScroll = totalScroll;
106 private Runnable mHideLineRunnable = new Runnable() {
109 animateLineToAlpha(0);
113 public PageIndicatorLineCaret(Context context) {
117 public PageIndicatorLineCaret(Context context, AttributeSet attrs) {
118 this(context, attrs, 0);
121 public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) {
122 super(context, attrs, defStyle);
124 Resources res = context.getResources();
125 mLinePaint = new Paint();
126 mLinePaint.setAlpha(0);
128 mLauncher = (Launcher) context;
129 mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
130 setCaretDrawable(new CaretDrawable(context));
134 protected void onFinishInflate() {
135 super.onFinishInflate();
136 mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
137 mAllAppsHandle.setImageDrawable(getCaretDrawable());
138 mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
139 mAllAppsHandle.setOnClickListener(mLauncher);
140 mAllAppsHandle.setOnLongClickListener(mLauncher);
141 mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
142 mLauncher.setAllAppsButton(mAllAppsHandle);
146 protected void onDraw(Canvas canvas) {
147 if (mTotalScroll == 0 || mNumPagesFloat == 0) {
151 // Compute and draw line rect.
152 float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
153 int availableWidth = canvas.getWidth();
154 int lineWidth = (int) (availableWidth / mNumPagesFloat);
155 int lineLeft = (int) (progress * (availableWidth - lineWidth));
156 int lineRight = lineLeft + lineWidth;
157 canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(),
162 public void setContentDescription(CharSequence contentDescription) {
163 mAllAppsHandle.setContentDescription(contentDescription);
167 public void setScroll(int currentScroll, int totalScroll) {
168 if (getAlpha() == 0) {
171 animateLineToAlpha(mActiveAlpha);
173 mCurrentScroll = currentScroll;
174 if (mTotalScroll == 0) {
175 mTotalScroll = totalScroll;
176 } else if (mTotalScroll != totalScroll) {
177 animateToTotalScroll(totalScroll);
182 if (mShouldAutoHide) {
187 private void hideAfterDelay() {
188 mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
189 mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
193 public void setActiveMarker(int activePage) {
197 protected void onPageCountChanged() {
198 if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
199 animateToNumPages(mNumPages);
203 public void setShouldAutoHide(boolean shouldAutoHide) {
204 mShouldAutoHide = shouldAutoHide;
205 if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
207 } else if (!shouldAutoHide) {
208 mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
213 * The line's color will be:
214 * - mostly opaque white if the hotseat is white (ignoring alpha)
215 * - mostly opaque black if the hotseat is black (ignoring alpha)
217 public void updateColor(ExtractedColors extractedColors) {
218 int originalLineAlpha = mLinePaint.getAlpha();
219 int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
220 if (color != Color.TRANSPARENT) {
221 color = ColorUtils.setAlphaComponent(color, 255);
222 if (color == Color.BLACK) {
223 mActiveAlpha = BLACK_ALPHA;
224 } else if (color == Color.WHITE) {
225 mActiveAlpha = WHITE_ALPHA;
227 Log.e(TAG, "Setting workspace page indicators to an unsupported color: #"
228 + Integer.toHexString(color));
230 mLinePaint.setColor(color);
231 mLinePaint.setAlpha(originalLineAlpha);
235 private void animateLineToAlpha(int alpha) {
236 if (alpha == mToAlpha) {
237 // Ignore the new animation if it is going to the same alpha as the current animation.
241 setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
242 LINE_ALPHA_ANIMATOR_INDEX);
245 private void animateToNumPages(int numPages) {
246 setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
247 NUM_PAGES_ANIMATOR_INDEX);
250 private void animateToTotalScroll(int totalScroll) {
251 setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
252 TOTAL_SCROLL_ANIMATOR_INDEX);
256 * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
257 * the animation ends.
259 * If an animator is already at the index (i.e. it is already playing), it is canceled and
260 * replaced with the new animator.
262 private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
263 if (mAnimators[animatorIndex] != null) {
264 mAnimators[animatorIndex].cancel();
266 mAnimators[animatorIndex] = animator;
267 mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
269 public void onAnimationEnd(Animator animation) {
270 mAnimators[animatorIndex] = null;
273 mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
274 mAnimators[animatorIndex].start();