| package com.android.launcher3.pageindicators; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.ObjectAnimator; |
| import android.animation.ValueAnimator; |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.Paint; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.support.v4.graphics.ColorUtils; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.util.Property; |
| import android.view.TouchDelegate; |
| import android.view.View; |
| import android.view.ViewConfiguration; |
| |
| import com.android.launcher3.Launcher; |
| import com.android.launcher3.R; |
| import com.android.launcher3.Utilities; |
| import com.android.launcher3.dynamicui.ExtractedColors; |
| |
| /** |
| * A PageIndicator that briefly shows a fraction of a line when moving between pages. |
| * |
| * The fraction is 1 / number of pages and the position is based on the progress of the page scroll. |
| */ |
| public class PageIndicatorLineCaret extends PageIndicator { |
| private static final String TAG = "PageIndicatorLine"; |
| |
| private static final int[] sTempCoords = new int[2]; |
| |
| private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration(); |
| private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay(); |
| public static final int WHITE_ALPHA = (int) (0.70f * 255); |
| public static final int BLACK_ALPHA = (int) (0.65f * 255); |
| |
| private static final int LINE_ALPHA_ANIMATOR_INDEX = 0; |
| private static final int NUM_PAGES_ANIMATOR_INDEX = 1; |
| private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2; |
| |
| private ValueAnimator[] mAnimators = new ValueAnimator[3]; |
| |
| private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper()); |
| |
| private boolean mShouldAutoHide = true; |
| |
| // The alpha of the line when it is showing. |
| private int mActiveAlpha = 0; |
| // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha). |
| private int mToAlpha; |
| // A float value representing the number of pages, to allow for an animation when it changes. |
| private float mNumPagesFloat; |
| private int mCurrentScroll; |
| private int mTotalScroll; |
| private Paint mLinePaint; |
| private Launcher mLauncher; |
| // all apps pull up handle drawable. |
| private final Drawable caretDrawable; |
| private final int mLineHeight; |
| private final Rect mTouchHitRect = new Rect(); |
| private final int mTouchExtensionHeight; |
| |
| private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA |
| = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") { |
| @Override |
| public Integer get(PageIndicatorLineCaret obj) { |
| return obj.mLinePaint.getAlpha(); |
| } |
| |
| @Override |
| public void set(PageIndicatorLineCaret obj, Integer alpha) { |
| obj.mLinePaint.setAlpha(alpha); |
| obj.invalidate(); |
| } |
| }; |
| |
| private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES |
| = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") { |
| @Override |
| public Float get(PageIndicatorLineCaret obj) { |
| return obj.mNumPagesFloat; |
| } |
| |
| @Override |
| public void set(PageIndicatorLineCaret obj, Float numPages) { |
| obj.mNumPagesFloat = numPages; |
| obj.invalidate(); |
| } |
| }; |
| |
| private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL |
| = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") { |
| @Override |
| public Integer get(PageIndicatorLineCaret obj) { |
| return obj.mTotalScroll; |
| } |
| |
| @Override |
| public void set(PageIndicatorLineCaret obj, Integer totalScroll) { |
| obj.mTotalScroll = totalScroll; |
| obj.invalidate(); |
| } |
| }; |
| |
| private Runnable mHideLineRunnable = new Runnable() { |
| @Override |
| public void run() { |
| animateLineToAlpha(0); |
| } |
| }; |
| |
| public PageIndicatorLineCaret(Context context) { |
| this(context, null); |
| } |
| |
| public PageIndicatorLineCaret(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| mLinePaint = new Paint(); |
| mLinePaint.setAlpha(0); |
| |
| mLauncher = (Launcher) context; |
| setOnTouchListener(mLauncher.getHapticFeedbackTouchListener()); |
| setOnClickListener(mLauncher); |
| setOnFocusChangeListener(mLauncher.mFocusHandler); |
| Resources res = context.getResources(); |
| caretDrawable = res.getDrawable(R.drawable.ic_allapps_caret); |
| mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height); |
| mTouchExtensionHeight = res.getDimensionPixelSize( |
| R.dimen.dynamic_grid_page_indicator_extra_touch_height); |
| } |
| |
| @Override |
| protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| super.onLayout(changed, left, top, right, bottom); |
| int size = bottom - top; |
| int l = (right - left) / 2 - size / 2; |
| caretDrawable.setBounds(l, 0, l+ size, size); |
| |
| // The touch area is expanded below this view by #mTouchExtensionHeight |
| // which extends to the top of the hotseat. |
| View parent = mLauncher.getDragLayer(); |
| sTempCoords[0] = sTempCoords[1] = 0; |
| Utilities.getDescendantCoordRelativeToParent(this, parent, sTempCoords, true); |
| mTouchHitRect.set(sTempCoords[0], sTempCoords[1], sTempCoords[0] + this.getWidth(), |
| sTempCoords[1] + getHeight() + mTouchExtensionHeight); |
| parent.setTouchDelegate(new TouchDelegate(mTouchHitRect, this)); |
| } |
| |
| @Override |
| protected void onDraw(Canvas canvas) { |
| caretDrawable.draw(canvas); |
| if (mTotalScroll == 0 || mNumPagesFloat == 0) { |
| return; |
| } |
| |
| // Compute and draw line rect. |
| float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f); |
| int availableWidth = canvas.getWidth(); |
| int lineWidth = (int) (availableWidth / mNumPagesFloat); |
| int lineLeft = (int) (progress * (availableWidth - lineWidth)); |
| int lineRight = lineLeft + lineWidth; |
| canvas.drawRect(lineLeft, canvas.getHeight() + mLineHeight, lineRight, canvas.getHeight(), |
| mLinePaint); |
| } |
| |
| @Override |
| public void setScroll(int currentScroll, int totalScroll) { |
| if (getAlpha() == 0) { |
| return; |
| } |
| animateLineToAlpha(mActiveAlpha); |
| |
| mCurrentScroll = currentScroll; |
| if (mTotalScroll == 0) { |
| mTotalScroll = totalScroll; |
| } else if (mTotalScroll != totalScroll) { |
| animateToTotalScroll(totalScroll); |
| } else { |
| invalidate(); |
| } |
| |
| if (mShouldAutoHide) { |
| hideAfterDelay(); |
| } |
| } |
| |
| private void hideAfterDelay() { |
| mDelayedLineFadeHandler.removeCallbacksAndMessages(null); |
| mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY); |
| } |
| |
| @Override |
| public void setActiveMarker(int activePage) { |
| } |
| |
| @Override |
| protected void onPageCountChanged() { |
| if (Float.compare(mNumPages, mNumPagesFloat) != 0) { |
| animateToNumPages(mNumPages); |
| } |
| } |
| |
| public void setShouldAutoHide(boolean shouldAutoHide) { |
| mShouldAutoHide = shouldAutoHide; |
| if (shouldAutoHide && mLinePaint.getAlpha() > 0) { |
| hideAfterDelay(); |
| } else if (!shouldAutoHide) { |
| mDelayedLineFadeHandler.removeCallbacksAndMessages(null); |
| } |
| } |
| |
| /** |
| * The line's color will be: |
| * - mostly opaque white if the hotseat is white (ignoring alpha) |
| * - mostly opaque black if the hotseat is black (ignoring alpha) |
| */ |
| public void updateColor(ExtractedColors extractedColors) { |
| int originalLineAlpha = mLinePaint.getAlpha(); |
| int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT); |
| if (color != Color.TRANSPARENT) { |
| color = ColorUtils.setAlphaComponent(color, 255); |
| if (color == Color.BLACK) { |
| mActiveAlpha = BLACK_ALPHA; |
| } else if (color == Color.WHITE) { |
| mActiveAlpha = WHITE_ALPHA; |
| } else { |
| Log.e(TAG, "Setting workspace page indicators to an unsupported color: #" |
| + Integer.toHexString(color)); |
| } |
| mLinePaint.setColor(color); |
| mLinePaint.setAlpha(originalLineAlpha); |
| } |
| } |
| |
| private void animateLineToAlpha(int alpha) { |
| if (alpha == mToAlpha) { |
| // Ignore the new animation if it is going to the same alpha as the current animation. |
| return; |
| } |
| mToAlpha = alpha; |
| setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha), |
| LINE_ALPHA_ANIMATOR_INDEX); |
| } |
| |
| private void animateToNumPages(int numPages) { |
| setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages), |
| NUM_PAGES_ANIMATOR_INDEX); |
| } |
| |
| private void animateToTotalScroll(int totalScroll) { |
| setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll), |
| TOTAL_SCROLL_ANIMATOR_INDEX); |
| } |
| |
| /** |
| * Starts the given animator and stores it in the provided index in {@link #mAnimators} until |
| * the animation ends. |
| * |
| * If an animator is already at the index (i.e. it is already playing), it is canceled and |
| * replaced with the new animator. |
| */ |
| private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) { |
| if (mAnimators[animatorIndex] != null) { |
| mAnimators[animatorIndex].cancel(); |
| } |
| mAnimators[animatorIndex] = animator; |
| mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| mAnimators[animatorIndex] = null; |
| } |
| }); |
| mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION); |
| mAnimators[animatorIndex].start(); |
| } |
| } |