1package com.android.launcher3.pageindicators; 2 3import android.animation.Animator; 4import android.animation.AnimatorListenerAdapter; 5import android.animation.ObjectAnimator; 6import android.animation.ValueAnimator; 7import android.content.Context; 8import android.content.res.Resources; 9import android.graphics.Canvas; 10import android.graphics.Color; 11import android.graphics.Paint; 12import android.os.Handler; 13import android.os.Looper; 14import android.support.v4.graphics.ColorUtils; 15import android.util.AttributeSet; 16import android.util.Log; 17import android.util.Property; 18import android.view.ViewConfiguration; 19import android.widget.ImageView; 20 21import com.android.launcher3.Launcher; 22import com.android.launcher3.R; 23import com.android.launcher3.Utilities; 24import com.android.launcher3.config.FeatureFlags; 25import com.android.launcher3.dynamicui.ExtractedColors; 26import com.android.launcher3.dynamicui.WallpaperColorInfo; 27 28/** 29 * A PageIndicator that briefly shows a fraction of a line when moving between pages. 30 * 31 * The fraction is 1 / number of pages and the position is based on the progress of the page scroll. 32 */ 33public class PageIndicatorLineCaret extends PageIndicator { 34 private static final String TAG = "PageIndicatorLine"; 35 36 private static final int[] sTempCoords = new int[2]; 37 38 private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration(); 39 private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay(); 40 public static final int WHITE_ALPHA = (int) (0.70f * 255); 41 public static final int BLACK_ALPHA = (int) (0.65f * 255); 42 43 private static final int LINE_ALPHA_ANIMATOR_INDEX = 0; 44 private static final int NUM_PAGES_ANIMATOR_INDEX = 1; 45 private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2; 46 47 private ValueAnimator[] mAnimators = new ValueAnimator[3]; 48 49 private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper()); 50 51 private boolean mShouldAutoHide = true; 52 53 // The alpha of the line when it is showing. 54 private int mActiveAlpha = 0; 55 // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha). 56 private int mToAlpha; 57 // A float value representing the number of pages, to allow for an animation when it changes. 58 private float mNumPagesFloat; 59 private int mCurrentScroll; 60 private int mTotalScroll; 61 private Paint mLinePaint; 62 private Launcher mLauncher; 63 private final int mLineHeight; 64 private ImageView mAllAppsHandle; 65 66 private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA 67 = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") { 68 @Override 69 public Integer get(PageIndicatorLineCaret obj) { 70 return obj.mLinePaint.getAlpha(); 71 } 72 73 @Override 74 public void set(PageIndicatorLineCaret obj, Integer alpha) { 75 obj.mLinePaint.setAlpha(alpha); 76 obj.invalidate(); 77 } 78 }; 79 80 private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES 81 = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") { 82 @Override 83 public Float get(PageIndicatorLineCaret obj) { 84 return obj.mNumPagesFloat; 85 } 86 87 @Override 88 public void set(PageIndicatorLineCaret obj, Float numPages) { 89 obj.mNumPagesFloat = numPages; 90 obj.invalidate(); 91 } 92 }; 93 94 private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL 95 = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") { 96 @Override 97 public Integer get(PageIndicatorLineCaret obj) { 98 return obj.mTotalScroll; 99 } 100 101 @Override 102 public void set(PageIndicatorLineCaret obj, Integer totalScroll) { 103 obj.mTotalScroll = totalScroll; 104 obj.invalidate(); 105 } 106 }; 107 108 private Runnable mHideLineRunnable = new Runnable() { 109 @Override 110 public void run() { 111 animateLineToAlpha(0); 112 } 113 }; 114 115 public PageIndicatorLineCaret(Context context) { 116 this(context, null); 117 } 118 119 public PageIndicatorLineCaret(Context context, AttributeSet attrs) { 120 this(context, attrs, 0); 121 } 122 123 public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) { 124 super(context, attrs, defStyle); 125 126 Resources res = context.getResources(); 127 mLinePaint = new Paint(); 128 mLinePaint.setAlpha(0); 129 130 mLauncher = Launcher.getLauncher(context); 131 mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height); 132 setCaretDrawable(new CaretDrawable(context)); 133 134 boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText(); 135 mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA; 136 mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE); 137 } 138 139 @Override 140 protected void onFinishInflate() { 141 super.onFinishInflate(); 142 mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle); 143 mAllAppsHandle.setImageDrawable(getCaretDrawable()); 144 mAllAppsHandle.setOnClickListener(mLauncher); 145 mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler); 146 mLauncher.setAllAppsButton(mAllAppsHandle); 147 } 148 149 @Override 150 public void setAccessibilityDelegate(AccessibilityDelegate delegate) { 151 mAllAppsHandle.setAccessibilityDelegate(delegate); 152 } 153 154 @Override 155 protected void onDraw(Canvas canvas) { 156 if (mTotalScroll == 0 || mNumPagesFloat == 0) { 157 return; 158 } 159 160 // Compute and draw line rect. 161 float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f); 162 int availableWidth = canvas.getWidth(); 163 int lineWidth = (int) (availableWidth / mNumPagesFloat); 164 int lineLeft = (int) (progress * (availableWidth - lineWidth)); 165 int lineRight = lineLeft + lineWidth; 166 canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(), 167 mLinePaint); 168 } 169 170 @Override 171 public void setContentDescription(CharSequence contentDescription) { 172 mAllAppsHandle.setContentDescription(contentDescription); 173 } 174 175 @Override 176 public void setScroll(int currentScroll, int totalScroll) { 177 if (getAlpha() == 0) { 178 return; 179 } 180 animateLineToAlpha(mActiveAlpha); 181 182 mCurrentScroll = currentScroll; 183 if (mTotalScroll == 0) { 184 mTotalScroll = totalScroll; 185 } else if (mTotalScroll != totalScroll) { 186 animateToTotalScroll(totalScroll); 187 } else { 188 invalidate(); 189 } 190 191 if (mShouldAutoHide) { 192 hideAfterDelay(); 193 } 194 } 195 196 private void hideAfterDelay() { 197 mDelayedLineFadeHandler.removeCallbacksAndMessages(null); 198 mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY); 199 } 200 201 @Override 202 public void setActiveMarker(int activePage) { 203 } 204 205 @Override 206 protected void onPageCountChanged() { 207 if (Float.compare(mNumPages, mNumPagesFloat) != 0) { 208 animateToNumPages(mNumPages); 209 } 210 } 211 212 public void setShouldAutoHide(boolean shouldAutoHide) { 213 mShouldAutoHide = shouldAutoHide; 214 if (shouldAutoHide && mLinePaint.getAlpha() > 0) { 215 hideAfterDelay(); 216 } else if (!shouldAutoHide) { 217 mDelayedLineFadeHandler.removeCallbacksAndMessages(null); 218 } 219 } 220 221 /** 222 * The line's color will be: 223 * - mostly opaque white if the hotseat is white (ignoring alpha) 224 * - mostly opaque black if the hotseat is black (ignoring alpha) 225 */ 226 public void updateColor(ExtractedColors extractedColors) { 227 if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) { 228 return; 229 } 230 int originalLineAlpha = mLinePaint.getAlpha(); 231 int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX); 232 if (color != Color.TRANSPARENT) { 233 color = ColorUtils.setAlphaComponent(color, 255); 234 if (color == Color.BLACK) { 235 mActiveAlpha = BLACK_ALPHA; 236 } else if (color == Color.WHITE) { 237 mActiveAlpha = WHITE_ALPHA; 238 } else { 239 Log.e(TAG, "Setting workspace page indicators to an unsupported color: #" 240 + Integer.toHexString(color)); 241 } 242 mLinePaint.setColor(color); 243 mLinePaint.setAlpha(originalLineAlpha); 244 } 245 } 246 247 private void animateLineToAlpha(int alpha) { 248 if (alpha == mToAlpha) { 249 // Ignore the new animation if it is going to the same alpha as the current animation. 250 return; 251 } 252 mToAlpha = alpha; 253 setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha), 254 LINE_ALPHA_ANIMATOR_INDEX); 255 } 256 257 private void animateToNumPages(int numPages) { 258 setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages), 259 NUM_PAGES_ANIMATOR_INDEX); 260 } 261 262 private void animateToTotalScroll(int totalScroll) { 263 setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll), 264 TOTAL_SCROLL_ANIMATOR_INDEX); 265 } 266 267 /** 268 * Starts the given animator and stores it in the provided index in {@link #mAnimators} until 269 * the animation ends. 270 * 271 * If an animator is already at the index (i.e. it is already playing), it is canceled and 272 * replaced with the new animator. 273 */ 274 private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) { 275 if (mAnimators[animatorIndex] != null) { 276 mAnimators[animatorIndex].cancel(); 277 } 278 mAnimators[animatorIndex] = animator; 279 mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() { 280 @Override 281 public void onAnimationEnd(Animator animation) { 282 mAnimators[animatorIndex] = null; 283 } 284 }); 285 mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION); 286 mAnimators[animatorIndex].start(); 287 } 288} 289