Loading core/java/com/android/internal/widget/NotificationProgressBar.java +15 −19 Original line number Diff line number Diff line Loading @@ -59,6 +59,8 @@ import java.util.TreeSet; public final class NotificationProgressBar extends ProgressBar { private static final String TAG = "NotificationProgressBar"; private NotificationProgressDrawable mNotificationProgressDrawable; private NotificationProgressModel mProgressModel; @Nullable Loading Loading @@ -94,6 +96,12 @@ public final class NotificationProgressBar extends ProgressBar { defStyleAttr, defStyleRes); try { mNotificationProgressDrawable = getNotificationProgressDrawable(); } catch (IllegalStateException ex) { Log.e(TAG, "Can't get NotificationProgressDrawable", ex); } // Supports setting the tracker in xml, but ProgressStyle notifications set/override it // via {@code setProgressTrackerIcon}. final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker); Loading Loading @@ -131,11 +139,8 @@ public final class NotificationProgressBar extends ProgressBar { progressMax, mProgressModel.isStyledByProgress()); try { final NotificationProgressDrawable drawable = getNotificationProgressDrawable(); drawable.setParts(mProgressDrawableParts); } catch (IllegalStateException ex) { Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex); if (mNotificationProgressDrawable != null) { mNotificationProgressDrawable.setParts(mProgressDrawableParts); } setMax(progressMax); Loading Loading @@ -195,10 +200,6 @@ public final class NotificationProgressBar extends ProgressBar { } private void setTracker(@Nullable Drawable tracker) { if (isIndeterminate() && tracker != null) { return; } final boolean needUpdate = mTracker != null && tracker != mTracker; if (needUpdate) { mTracker.setCallback(null); Loading @@ -222,6 +223,9 @@ public final class NotificationProgressBar extends ProgressBar { } mTracker = tracker; if (mNotificationProgressDrawable != null) { mNotificationProgressDrawable.setHasTrackerIcon(mTracker != null); } configureTrackerBounds(); Loading Loading @@ -274,16 +278,6 @@ public final class NotificationProgressBar extends ProgressBar { mTrackerDrawMatrix.postTranslate(Math.round(dx), Math.round(dy)); } @Override @RemotableViewMethod public synchronized void setIndeterminate(boolean indeterminate) { super.setIndeterminate(indeterminate); if (isIndeterminate()) { setTracker(null); } } @Override protected boolean verifyDrawable(@NonNull Drawable who) { return who == mTracker || super.verifyDrawable(who); Loading Loading @@ -421,6 +415,8 @@ public final class NotificationProgressBar extends ProgressBar { @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); if (isIndeterminate()) return; drawTracker(canvas); } Loading core/java/com/android/internal/widget/NotificationProgressDrawable.java +102 −157 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; Loading Loading @@ -62,21 +61,15 @@ public final class NotificationProgressDrawable extends Drawable { private boolean mMutated; private final ArrayList<Part> mParts = new ArrayList<>(); private boolean mHasTrackerIcon; private final RectF mSegRectF = new RectF(); private final Rect mPointRect = new Rect(); private final RectF mPointRectF = new RectF(); private final Paint mStrokePaint = new Paint(); private final Paint mDashedStrokePaint = new Paint(); private final Paint mFillPaint = new Paint(); { mStrokePaint.setStyle(Paint.Style.STROKE); mStrokePaint.setStrokeCap(Paint.Cap.ROUND); mDashedStrokePaint.setStyle(Paint.Style.STROKE); mFillPaint.setStyle(Paint.Style.FILL); } Loading @@ -87,49 +80,15 @@ public final class NotificationProgressDrawable extends Drawable { } /** * <p>Set the stroke width and default color for the drawable.</p> * <p>Note: changing this property will affect all instances of a drawable loaded from a * resource. It is recommended to invoke * {@link #mutate()} before changing this property.</p> * * @param width The width in pixels of the stroke * @param color The color of the stroke * @see #mutate() * @see #setStroke(int, int, float, float) */ public void setStroke(int width, @ColorInt int color) { setStroke(width, color, 0, 0); } /** * <p>Set the stroke width and default color for the drawable. This method can also be used * to dash the stroke for the dashed segments.</p> * <p>Set the segment default color for the drawable.</p> * <p>Note: changing this property will affect all instances of a drawable loaded from a * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> * * @param width The width in pixels of the stroke * @param color The color of the stroke * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes * @param dashGap The gap in pixels between dashes * @see #mutate() * @see #setStroke(int, int) */ public void setStroke(int width, @ColorInt int color, float dashWidth, float dashGap) { mState.setStroke(width, color, dashWidth, dashGap); setStrokeInternal(width, dashWidth, dashGap); } /** * <p>Set the stroke default color for the drawable.</p> * <p>Note: changing this property will affect all instances of a drawable loaded from a * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> * * @param color The color of the stroke * @see #mutate() * @see #setStroke(int, int, float, float) */ public void setStrokeDefaultColor(@ColorInt int color) { mState.setStrokeColor(color); public void setSegmentDefaultColor(@ColorInt int color) { mState.setSegmentColor(color); } /** Loading @@ -144,25 +103,14 @@ public final class NotificationProgressDrawable extends Drawable { mState.setPointRectColor(color); } private void setStrokeInternal(int width, float dashWidth, float dashGap) { mStrokePaint.setStrokeWidth(width); mDashedStrokePaint.setStrokeWidth(width); DashPathEffect e = null; if (dashWidth > 0) { e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0); } mDashedStrokePaint.setPathEffect(e); invalidateSelf(); } /** * Set the segments and points that constitute the drawable. */ public void setParts(List<Part> parts) { mParts.clear(); mParts.addAll(parts); invalidateSelf(); } /** Loading @@ -172,6 +120,16 @@ public final class NotificationProgressDrawable extends Drawable { setParts(Arrays.asList(parts)); } /** * Set whether a tracker is drawn on top of this NotificationProgressDrawable. */ public void setHasTrackerIcon(boolean hasTrackerIcon) { if (mHasTrackerIcon != hasTrackerIcon) { mHasTrackerIcon = hasTrackerIcon; invalidateSelf(); } } @Override public void draw(@NonNull Canvas canvas) { final float pointRadius = Loading @@ -181,6 +139,7 @@ public final class NotificationProgressDrawable extends Drawable { float x = (float) getBounds().left; final float centerY = (float) getBounds().centerY(); final float totalWidth = (float) getBounds().width(); float segPointGap = mState.mSegPointGap; final int numParts = mParts.size(); for (int iPart = 0; iPart < numParts; iPart++) { Loading @@ -188,15 +147,19 @@ public final class NotificationProgressDrawable extends Drawable { final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1); final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1); if (part instanceof Segment segment) { // Update the segment-point gap to 2X upon seeing the first faded segment. // (Assuming that all segments before are solid, and all segments after are faded.) if (segment.mFaded) { segPointGap = mState.mSegPointGap * 2; } final float segWidth = segment.mFraction * totalWidth; // Advance the start position to account for a point immediately prior. final float startOffset = getSegStartOffset(prevPart, pointRadius, mState.mSegPointGap, x); final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x); final float start = x + startOffset; // Retract the end position to account for the padding and a point immediately // after. final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap, mState.mSegSegGap, x + segWidth, totalWidth); final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap, mState.mSegSegGap, x + segWidth, totalWidth, mHasTrackerIcon); final float end = x + segWidth - endOffset; // Advance the current position to account for the segment's fraction of the total Loading @@ -206,35 +169,15 @@ public final class NotificationProgressDrawable extends Drawable { // No space left to draw the segment if (start > end) continue; if (segment.mDashed) { // No caps when the segment is dashed. mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor : mState.mFadedStrokeColor); canvas.drawLine(start, centerY, end, centerY, mDashedStrokePaint); } else if (end - start < mState.mStrokeWidth) { // Not enough segment length to draw the caps final float rad = (end - start) / 2F; final float capWidth = mStrokePaint.getStrokeWidth() / 2F; final float radiusY = segment.mFaded ? mState.mFadedSegmentHeight / 2F : mState.mSegmentHeight / 2F; final float cornerRadius = mState.mSegmentCornerRadius; mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor : mState.mStrokeColor); mSegRectF.set(start, centerY - capWidth, end, centerY + capWidth); canvas.drawRoundRect(mSegRectF, rad, rad, mFillPaint); } else { // Leave space for the rounded line cap which extends beyond start/end. final float capWidth = mStrokePaint.getStrokeWidth() / 2F; // Transparent is not allowed (and also is the default in the data), so use that // as a sentinel to be replaced by default mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor : mState.mStrokeColor); : (segment.mFaded ? mState.mFadedSegmentColor : mState.mSegmentColor)); canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY, mStrokePaint); } mSegRectF.set(start, centerY - radiusY, end, centerY + radiusY); canvas.drawRoundRect(mSegRectF, cornerRadius, cornerRadius, mFillPaint); } else if (part instanceof Point point) { final float pointWidth = 2 * pointRadius; float start = x - pointRadius; Loading Loading @@ -275,10 +218,17 @@ public final class NotificationProgressDrawable extends Drawable { return pointOffset + pointRadius + segPointGap; } private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap, float segSegGap, float endX, float totalWidth) { private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius, float segPointGap, float segSegGap, float endX, float totalWidth, boolean hasTrackerIcon) { if (nextPart == null) return 0F; if (!(nextPart instanceof Point)) return segSegGap; if (nextPart instanceof Segment nextSeg) { if (!seg.mFaded && nextSeg.mFaded) { // @see Segment#mFaded return hasTrackerIcon ? 0F : segSegGap * 4F; } return segSegGap; } final float pointWidth = 2 * pointRadius; final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth) Loading Loading @@ -439,21 +389,17 @@ public final class NotificationProgressDrawable extends Drawable { // Extract the theme attributes, if any. state.mThemeAttrsSegments = a.extractThemeAttrs(); final int width = a.getDimensionPixelSize( R.styleable.NotificationProgressDrawableSegments_width, state.mStrokeWidth); final float dashWidth = a.getDimension( R.styleable.NotificationProgressDrawableSegments_dashWidth, state.mStrokeDashWidth); state.mSegmentHeight = a.getDimension( R.styleable.NotificationProgressDrawableSegments_height, state.mSegmentHeight); state.mFadedSegmentHeight = a.getDimension( R.styleable.NotificationProgressDrawableSegments_fadedHeight, state.mFadedSegmentHeight); state.mSegmentCornerRadius = a.getDimension( R.styleable.NotificationProgressDrawableSegments_cornerRadius, state.mSegmentCornerRadius); final int color = a.getColor(R.styleable.NotificationProgressDrawableSegments_color, state.mStrokeColor); if (dashWidth != 0.0f) { final float dashGap = a.getDimension( R.styleable.NotificationProgressDrawableSegments_dashGap, state.mStrokeDashGap); setStroke(width, color, dashWidth, dashGap); } else { setStroke(width, color); } state.mSegmentColor); setSegmentDefaultColor(color); } private void updatePointsFromTypedArray(TypedArray a) { Loading Loading @@ -532,11 +478,24 @@ public final class NotificationProgressDrawable extends Drawable { /** * A segment is a part of the progress bar with non-zero length. For example, it can * represent a portion in a navigation journey with certain traffic condition. * */ public static final class Segment implements Part { private final float mFraction; @ColorInt private final int mColor; private final boolean mDashed; /** Whether the segment is faded or not. * <p> * <pre> * When mFaded is set to true, a combination of the following is done to the segment: * 1. The drawing color is mColor with opacity updated to 15%. * 2. The segment-point gap is 2X the segment-point gap for non-faded segments. * 3. The gap between faded and non-faded segments is: * 4X the segment-segment gap, when there is no tracker icon * 0, when there is tracker icon * </pre> * </p> */ private final boolean mFaded; public Segment(float fraction) { this(fraction, Color.TRANSPARENT); Loading @@ -546,10 +505,10 @@ public final class NotificationProgressDrawable extends Drawable { this(fraction, color, false); } public Segment(float fraction, @ColorInt int color, boolean dashed) { public Segment(float fraction, @ColorInt int color, boolean faded) { mFraction = fraction; mColor = color; mDashed = dashed; mFaded = faded; } public float getFraction() { Loading @@ -560,14 +519,14 @@ public final class NotificationProgressDrawable extends Drawable { return this.mColor; } public boolean getDashed() { return this.mDashed; public boolean getFaded() { return this.mFaded; } @Override public String toString() { return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", dashed=" + this.mDashed + ')'; return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", faded=" + this.mFaded + ')'; } // Needed for unit tests Loading @@ -580,12 +539,12 @@ public final class NotificationProgressDrawable extends Drawable { Segment that = (Segment) other; if (Float.compare(this.mFraction, that.mFraction) != 0) return false; if (this.mColor != that.mColor) return false; return this.mDashed == that.mDashed; return this.mFaded == that.mFaded; } @Override public int hashCode() { return Objects.hash(mFraction, mColor, mDashed); return Objects.hash(mFraction, mColor, mFaded); } } Loading Loading @@ -675,11 +634,11 @@ public final class NotificationProgressDrawable extends Drawable { int mChangingConfigurations; float mSegSegGap = 0.0f; float mSegPointGap = 0.0f; int mStrokeWidth = 0; int mStrokeColor; int mFadedStrokeColor; float mStrokeDashWidth = 0.0f; float mStrokeDashGap = 0.0f; float mSegmentHeight; float mFadedSegmentHeight; float mSegmentCornerRadius; int mSegmentColor; int mFadedSegmentColor; float mPointRadius; float mPointRectInset; float mPointRectCornerRadius; Loading @@ -699,11 +658,11 @@ public final class NotificationProgressDrawable extends Drawable { mChangingConfigurations = orig.mChangingConfigurations; mSegSegGap = orig.mSegSegGap; mSegPointGap = orig.mSegPointGap; mStrokeWidth = orig.mStrokeWidth; mStrokeColor = orig.mStrokeColor; mFadedStrokeColor = orig.mFadedStrokeColor; mStrokeDashWidth = orig.mStrokeDashWidth; mStrokeDashGap = orig.mStrokeDashGap; mSegmentHeight = orig.mSegmentHeight; mFadedSegmentHeight = orig.mFadedSegmentHeight; mSegmentCornerRadius = orig.mSegmentCornerRadius; mSegmentColor = orig.mSegmentColor; mFadedSegmentColor = orig.mFadedSegmentColor; mPointRadius = orig.mPointRadius; mPointRectInset = orig.mPointRectInset; mPointRectCornerRadius = orig.mPointRectCornerRadius; Loading @@ -721,17 +680,17 @@ public final class NotificationProgressDrawable extends Drawable { } private void applyDensityScaling(int sourceDensity, int targetDensity) { if (mStrokeWidth > 0) { mStrokeWidth = scaleFromDensity( mStrokeWidth, sourceDensity, targetDensity, true); if (mSegmentHeight > 0) { mSegmentHeight = scaleFromDensity( mSegmentHeight, sourceDensity, targetDensity); } if (mStrokeDashWidth > 0) { mStrokeDashWidth = scaleFromDensity( mStrokeDashWidth, sourceDensity, targetDensity); if (mFadedSegmentHeight > 0) { mFadedSegmentHeight = scaleFromDensity( mFadedSegmentHeight, sourceDensity, targetDensity); } if (mStrokeDashGap > 0) { mStrokeDashGap = scaleFromDensity( mStrokeDashGap, sourceDensity, targetDensity); if (mSegmentCornerRadius > 0) { mSegmentCornerRadius = scaleFromDensity( mSegmentCornerRadius, sourceDensity, targetDensity); } if (mPointRadius > 0) { mPointRadius = scaleFromDensity( Loading Loading @@ -788,17 +747,9 @@ public final class NotificationProgressDrawable extends Drawable { } } public void setStroke(int width, int color, float dashWidth, float dashGap) { mStrokeWidth = width; mStrokeDashWidth = dashWidth; mStrokeDashGap = dashGap; setStrokeColor(color); } public void setStrokeColor(int color) { mStrokeColor = color; mFadedStrokeColor = getFadedColor(color); public void setSegmentColor(int color) { mSegmentColor = color; mFadedSegmentColor = getFadedColor(color); } public void setPointRectColor(int color) { Loading @@ -808,11 +759,14 @@ public final class NotificationProgressDrawable extends Drawable { } /** * Get a color with an opacity that's 50% of the input color. * Get a color with an opacity that's 25% of the input color. */ @ColorInt static int getFadedColor(@ColorInt int color) { return Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color), return Color.argb( (int) (Color.alpha(color) * 0.25f + 0.5f), Color.red(color), Color.green(color), Color.blue(color)); } Loading @@ -836,15 +790,6 @@ public final class NotificationProgressDrawable extends Drawable { } private void updateLocalState() { final State state = mState; mStrokePaint.setStrokeWidth(state.mStrokeWidth); mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth); if (state.mStrokeDashWidth != 0.0f) { final DashPathEffect e = new DashPathEffect( new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0); mDashedStrokePaint.setPathEffect(e); } // NO-OP } } core/res/res/drawable/notification_progress.xml +3 −3 Original line number Diff line number Diff line Loading @@ -24,9 +24,9 @@ android:segPointGap="@dimen/notification_progress_segPoint_gap"> <segments android:color="?attr/colorProgressBackgroundNormal" android:dashGap="@dimen/notification_progress_segments_dash_gap" android:dashWidth="@dimen/notification_progress_segments_dash_width" android:width="@dimen/notification_progress_segments_height" /> android:height="@dimen/notification_progress_segments_height" android:fadedHeight="@dimen/notification_progress_segments_faded_height" android:cornerRadius="@dimen/notification_progress_segments_corner_radius"/> <points android:color="?attr/colorProgressBackgroundNormal" android:radius="@dimen/notification_progress_points_radius" Loading core/res/res/values/attrs.xml +8 −8 Original line number Diff line number Diff line Loading @@ -7748,14 +7748,14 @@ <!-- Used to config the segments of a NotificationProgressDrawable. --> <!-- @hide internal use only --> <declare-styleable name="NotificationProgressDrawableSegments"> <!-- Width of the stroke. --> <attr name="width" /> <!-- Default color of the stroke. --> <!-- Height of the solid segments --> <attr name="height" /> <!-- Height of the faded segments --> <attr name="fadedHeight" format="dimension"/> <!-- Corner radius of the segment rect. --> <attr name="cornerRadius" format="dimension" /> <!-- Default color of the segment. --> <attr name="color" /> <!-- Length of a dash in the stroke for the dashed segments. --> <attr name="dashWidth" /> <!-- Gap between dashes in the stroke for the dashed segments. --> <attr name="dashGap" /> </declare-styleable> <!-- Used to config the points of a NotificationProgressDrawable. --> Loading @@ -7766,7 +7766,7 @@ <!-- Inset of the point icon or rect. --> <attr name="inset" /> <!-- Corner radius of the point rect. --> <attr name="cornerRadius" format="dimension" /> <attr name="cornerRadius"/> <!-- Default color of the point rect. --> <attr name="color" /> </declare-styleable> Loading core/res/res/values/dimens.xml +5 −5 Original line number Diff line number Diff line Loading @@ -838,13 +838,13 @@ <!-- The gap between segments in the notification progress bar --> <dimen name="notification_progress_segSeg_gap">2dp</dimen> <!-- The gap between a segment and a point in the notification progress bar --> <dimen name="notification_progress_segPoint_gap">8dp</dimen> <!-- The dash gap of the notification progress bar segments --> <dimen name="notification_progress_segments_dash_gap">8dp</dimen> <!-- The dash width of the notification progress bar segments --> <dimen name="notification_progress_segments_dash_width">3dp</dimen> <dimen name="notification_progress_segPoint_gap">4dp</dimen> <!-- The height of the notification progress bar segments --> <dimen name="notification_progress_segments_height">6dp</dimen> <!-- The height of the notification progress bar faded segments --> <dimen name="notification_progress_segments_faded_height">2dp</dimen> <!-- The corner radius of the notification progress bar segments --> <dimen name="notification_progress_segments_corner_radius">16dp</dimen> <!-- The radius of the notification progress bar points --> <dimen name="notification_progress_points_radius">6dp</dimen> <!-- The corner radius of the notification progress bar points drawn as rects --> Loading Loading
core/java/com/android/internal/widget/NotificationProgressBar.java +15 −19 Original line number Diff line number Diff line Loading @@ -59,6 +59,8 @@ import java.util.TreeSet; public final class NotificationProgressBar extends ProgressBar { private static final String TAG = "NotificationProgressBar"; private NotificationProgressDrawable mNotificationProgressDrawable; private NotificationProgressModel mProgressModel; @Nullable Loading Loading @@ -94,6 +96,12 @@ public final class NotificationProgressBar extends ProgressBar { defStyleAttr, defStyleRes); try { mNotificationProgressDrawable = getNotificationProgressDrawable(); } catch (IllegalStateException ex) { Log.e(TAG, "Can't get NotificationProgressDrawable", ex); } // Supports setting the tracker in xml, but ProgressStyle notifications set/override it // via {@code setProgressTrackerIcon}. final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker); Loading Loading @@ -131,11 +139,8 @@ public final class NotificationProgressBar extends ProgressBar { progressMax, mProgressModel.isStyledByProgress()); try { final NotificationProgressDrawable drawable = getNotificationProgressDrawable(); drawable.setParts(mProgressDrawableParts); } catch (IllegalStateException ex) { Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex); if (mNotificationProgressDrawable != null) { mNotificationProgressDrawable.setParts(mProgressDrawableParts); } setMax(progressMax); Loading Loading @@ -195,10 +200,6 @@ public final class NotificationProgressBar extends ProgressBar { } private void setTracker(@Nullable Drawable tracker) { if (isIndeterminate() && tracker != null) { return; } final boolean needUpdate = mTracker != null && tracker != mTracker; if (needUpdate) { mTracker.setCallback(null); Loading @@ -222,6 +223,9 @@ public final class NotificationProgressBar extends ProgressBar { } mTracker = tracker; if (mNotificationProgressDrawable != null) { mNotificationProgressDrawable.setHasTrackerIcon(mTracker != null); } configureTrackerBounds(); Loading Loading @@ -274,16 +278,6 @@ public final class NotificationProgressBar extends ProgressBar { mTrackerDrawMatrix.postTranslate(Math.round(dx), Math.round(dy)); } @Override @RemotableViewMethod public synchronized void setIndeterminate(boolean indeterminate) { super.setIndeterminate(indeterminate); if (isIndeterminate()) { setTracker(null); } } @Override protected boolean verifyDrawable(@NonNull Drawable who) { return who == mTracker || super.verifyDrawable(who); Loading Loading @@ -421,6 +415,8 @@ public final class NotificationProgressBar extends ProgressBar { @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); if (isIndeterminate()) return; drawTracker(canvas); } Loading
core/java/com/android/internal/widget/NotificationProgressDrawable.java +102 −157 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; Loading Loading @@ -62,21 +61,15 @@ public final class NotificationProgressDrawable extends Drawable { private boolean mMutated; private final ArrayList<Part> mParts = new ArrayList<>(); private boolean mHasTrackerIcon; private final RectF mSegRectF = new RectF(); private final Rect mPointRect = new Rect(); private final RectF mPointRectF = new RectF(); private final Paint mStrokePaint = new Paint(); private final Paint mDashedStrokePaint = new Paint(); private final Paint mFillPaint = new Paint(); { mStrokePaint.setStyle(Paint.Style.STROKE); mStrokePaint.setStrokeCap(Paint.Cap.ROUND); mDashedStrokePaint.setStyle(Paint.Style.STROKE); mFillPaint.setStyle(Paint.Style.FILL); } Loading @@ -87,49 +80,15 @@ public final class NotificationProgressDrawable extends Drawable { } /** * <p>Set the stroke width and default color for the drawable.</p> * <p>Note: changing this property will affect all instances of a drawable loaded from a * resource. It is recommended to invoke * {@link #mutate()} before changing this property.</p> * * @param width The width in pixels of the stroke * @param color The color of the stroke * @see #mutate() * @see #setStroke(int, int, float, float) */ public void setStroke(int width, @ColorInt int color) { setStroke(width, color, 0, 0); } /** * <p>Set the stroke width and default color for the drawable. This method can also be used * to dash the stroke for the dashed segments.</p> * <p>Set the segment default color for the drawable.</p> * <p>Note: changing this property will affect all instances of a drawable loaded from a * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> * * @param width The width in pixels of the stroke * @param color The color of the stroke * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes * @param dashGap The gap in pixels between dashes * @see #mutate() * @see #setStroke(int, int) */ public void setStroke(int width, @ColorInt int color, float dashWidth, float dashGap) { mState.setStroke(width, color, dashWidth, dashGap); setStrokeInternal(width, dashWidth, dashGap); } /** * <p>Set the stroke default color for the drawable.</p> * <p>Note: changing this property will affect all instances of a drawable loaded from a * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p> * * @param color The color of the stroke * @see #mutate() * @see #setStroke(int, int, float, float) */ public void setStrokeDefaultColor(@ColorInt int color) { mState.setStrokeColor(color); public void setSegmentDefaultColor(@ColorInt int color) { mState.setSegmentColor(color); } /** Loading @@ -144,25 +103,14 @@ public final class NotificationProgressDrawable extends Drawable { mState.setPointRectColor(color); } private void setStrokeInternal(int width, float dashWidth, float dashGap) { mStrokePaint.setStrokeWidth(width); mDashedStrokePaint.setStrokeWidth(width); DashPathEffect e = null; if (dashWidth > 0) { e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0); } mDashedStrokePaint.setPathEffect(e); invalidateSelf(); } /** * Set the segments and points that constitute the drawable. */ public void setParts(List<Part> parts) { mParts.clear(); mParts.addAll(parts); invalidateSelf(); } /** Loading @@ -172,6 +120,16 @@ public final class NotificationProgressDrawable extends Drawable { setParts(Arrays.asList(parts)); } /** * Set whether a tracker is drawn on top of this NotificationProgressDrawable. */ public void setHasTrackerIcon(boolean hasTrackerIcon) { if (mHasTrackerIcon != hasTrackerIcon) { mHasTrackerIcon = hasTrackerIcon; invalidateSelf(); } } @Override public void draw(@NonNull Canvas canvas) { final float pointRadius = Loading @@ -181,6 +139,7 @@ public final class NotificationProgressDrawable extends Drawable { float x = (float) getBounds().left; final float centerY = (float) getBounds().centerY(); final float totalWidth = (float) getBounds().width(); float segPointGap = mState.mSegPointGap; final int numParts = mParts.size(); for (int iPart = 0; iPart < numParts; iPart++) { Loading @@ -188,15 +147,19 @@ public final class NotificationProgressDrawable extends Drawable { final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1); final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1); if (part instanceof Segment segment) { // Update the segment-point gap to 2X upon seeing the first faded segment. // (Assuming that all segments before are solid, and all segments after are faded.) if (segment.mFaded) { segPointGap = mState.mSegPointGap * 2; } final float segWidth = segment.mFraction * totalWidth; // Advance the start position to account for a point immediately prior. final float startOffset = getSegStartOffset(prevPart, pointRadius, mState.mSegPointGap, x); final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x); final float start = x + startOffset; // Retract the end position to account for the padding and a point immediately // after. final float endOffset = getSegEndOffset(nextPart, pointRadius, mState.mSegPointGap, mState.mSegSegGap, x + segWidth, totalWidth); final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap, mState.mSegSegGap, x + segWidth, totalWidth, mHasTrackerIcon); final float end = x + segWidth - endOffset; // Advance the current position to account for the segment's fraction of the total Loading @@ -206,35 +169,15 @@ public final class NotificationProgressDrawable extends Drawable { // No space left to draw the segment if (start > end) continue; if (segment.mDashed) { // No caps when the segment is dashed. mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor : mState.mFadedStrokeColor); canvas.drawLine(start, centerY, end, centerY, mDashedStrokePaint); } else if (end - start < mState.mStrokeWidth) { // Not enough segment length to draw the caps final float rad = (end - start) / 2F; final float capWidth = mStrokePaint.getStrokeWidth() / 2F; final float radiusY = segment.mFaded ? mState.mFadedSegmentHeight / 2F : mState.mSegmentHeight / 2F; final float cornerRadius = mState.mSegmentCornerRadius; mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor : mState.mStrokeColor); mSegRectF.set(start, centerY - capWidth, end, centerY + capWidth); canvas.drawRoundRect(mSegRectF, rad, rad, mFillPaint); } else { // Leave space for the rounded line cap which extends beyond start/end. final float capWidth = mStrokePaint.getStrokeWidth() / 2F; // Transparent is not allowed (and also is the default in the data), so use that // as a sentinel to be replaced by default mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor : mState.mStrokeColor); : (segment.mFaded ? mState.mFadedSegmentColor : mState.mSegmentColor)); canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY, mStrokePaint); } mSegRectF.set(start, centerY - radiusY, end, centerY + radiusY); canvas.drawRoundRect(mSegRectF, cornerRadius, cornerRadius, mFillPaint); } else if (part instanceof Point point) { final float pointWidth = 2 * pointRadius; float start = x - pointRadius; Loading Loading @@ -275,10 +218,17 @@ public final class NotificationProgressDrawable extends Drawable { return pointOffset + pointRadius + segPointGap; } private static float getSegEndOffset(Part nextPart, float pointRadius, float segPointGap, float segSegGap, float endX, float totalWidth) { private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius, float segPointGap, float segSegGap, float endX, float totalWidth, boolean hasTrackerIcon) { if (nextPart == null) return 0F; if (!(nextPart instanceof Point)) return segSegGap; if (nextPart instanceof Segment nextSeg) { if (!seg.mFaded && nextSeg.mFaded) { // @see Segment#mFaded return hasTrackerIcon ? 0F : segSegGap * 4F; } return segSegGap; } final float pointWidth = 2 * pointRadius; final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth) Loading Loading @@ -439,21 +389,17 @@ public final class NotificationProgressDrawable extends Drawable { // Extract the theme attributes, if any. state.mThemeAttrsSegments = a.extractThemeAttrs(); final int width = a.getDimensionPixelSize( R.styleable.NotificationProgressDrawableSegments_width, state.mStrokeWidth); final float dashWidth = a.getDimension( R.styleable.NotificationProgressDrawableSegments_dashWidth, state.mStrokeDashWidth); state.mSegmentHeight = a.getDimension( R.styleable.NotificationProgressDrawableSegments_height, state.mSegmentHeight); state.mFadedSegmentHeight = a.getDimension( R.styleable.NotificationProgressDrawableSegments_fadedHeight, state.mFadedSegmentHeight); state.mSegmentCornerRadius = a.getDimension( R.styleable.NotificationProgressDrawableSegments_cornerRadius, state.mSegmentCornerRadius); final int color = a.getColor(R.styleable.NotificationProgressDrawableSegments_color, state.mStrokeColor); if (dashWidth != 0.0f) { final float dashGap = a.getDimension( R.styleable.NotificationProgressDrawableSegments_dashGap, state.mStrokeDashGap); setStroke(width, color, dashWidth, dashGap); } else { setStroke(width, color); } state.mSegmentColor); setSegmentDefaultColor(color); } private void updatePointsFromTypedArray(TypedArray a) { Loading Loading @@ -532,11 +478,24 @@ public final class NotificationProgressDrawable extends Drawable { /** * A segment is a part of the progress bar with non-zero length. For example, it can * represent a portion in a navigation journey with certain traffic condition. * */ public static final class Segment implements Part { private final float mFraction; @ColorInt private final int mColor; private final boolean mDashed; /** Whether the segment is faded or not. * <p> * <pre> * When mFaded is set to true, a combination of the following is done to the segment: * 1. The drawing color is mColor with opacity updated to 15%. * 2. The segment-point gap is 2X the segment-point gap for non-faded segments. * 3. The gap between faded and non-faded segments is: * 4X the segment-segment gap, when there is no tracker icon * 0, when there is tracker icon * </pre> * </p> */ private final boolean mFaded; public Segment(float fraction) { this(fraction, Color.TRANSPARENT); Loading @@ -546,10 +505,10 @@ public final class NotificationProgressDrawable extends Drawable { this(fraction, color, false); } public Segment(float fraction, @ColorInt int color, boolean dashed) { public Segment(float fraction, @ColorInt int color, boolean faded) { mFraction = fraction; mColor = color; mDashed = dashed; mFaded = faded; } public float getFraction() { Loading @@ -560,14 +519,14 @@ public final class NotificationProgressDrawable extends Drawable { return this.mColor; } public boolean getDashed() { return this.mDashed; public boolean getFaded() { return this.mFaded; } @Override public String toString() { return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", dashed=" + this.mDashed + ')'; return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", faded=" + this.mFaded + ')'; } // Needed for unit tests Loading @@ -580,12 +539,12 @@ public final class NotificationProgressDrawable extends Drawable { Segment that = (Segment) other; if (Float.compare(this.mFraction, that.mFraction) != 0) return false; if (this.mColor != that.mColor) return false; return this.mDashed == that.mDashed; return this.mFaded == that.mFaded; } @Override public int hashCode() { return Objects.hash(mFraction, mColor, mDashed); return Objects.hash(mFraction, mColor, mFaded); } } Loading Loading @@ -675,11 +634,11 @@ public final class NotificationProgressDrawable extends Drawable { int mChangingConfigurations; float mSegSegGap = 0.0f; float mSegPointGap = 0.0f; int mStrokeWidth = 0; int mStrokeColor; int mFadedStrokeColor; float mStrokeDashWidth = 0.0f; float mStrokeDashGap = 0.0f; float mSegmentHeight; float mFadedSegmentHeight; float mSegmentCornerRadius; int mSegmentColor; int mFadedSegmentColor; float mPointRadius; float mPointRectInset; float mPointRectCornerRadius; Loading @@ -699,11 +658,11 @@ public final class NotificationProgressDrawable extends Drawable { mChangingConfigurations = orig.mChangingConfigurations; mSegSegGap = orig.mSegSegGap; mSegPointGap = orig.mSegPointGap; mStrokeWidth = orig.mStrokeWidth; mStrokeColor = orig.mStrokeColor; mFadedStrokeColor = orig.mFadedStrokeColor; mStrokeDashWidth = orig.mStrokeDashWidth; mStrokeDashGap = orig.mStrokeDashGap; mSegmentHeight = orig.mSegmentHeight; mFadedSegmentHeight = orig.mFadedSegmentHeight; mSegmentCornerRadius = orig.mSegmentCornerRadius; mSegmentColor = orig.mSegmentColor; mFadedSegmentColor = orig.mFadedSegmentColor; mPointRadius = orig.mPointRadius; mPointRectInset = orig.mPointRectInset; mPointRectCornerRadius = orig.mPointRectCornerRadius; Loading @@ -721,17 +680,17 @@ public final class NotificationProgressDrawable extends Drawable { } private void applyDensityScaling(int sourceDensity, int targetDensity) { if (mStrokeWidth > 0) { mStrokeWidth = scaleFromDensity( mStrokeWidth, sourceDensity, targetDensity, true); if (mSegmentHeight > 0) { mSegmentHeight = scaleFromDensity( mSegmentHeight, sourceDensity, targetDensity); } if (mStrokeDashWidth > 0) { mStrokeDashWidth = scaleFromDensity( mStrokeDashWidth, sourceDensity, targetDensity); if (mFadedSegmentHeight > 0) { mFadedSegmentHeight = scaleFromDensity( mFadedSegmentHeight, sourceDensity, targetDensity); } if (mStrokeDashGap > 0) { mStrokeDashGap = scaleFromDensity( mStrokeDashGap, sourceDensity, targetDensity); if (mSegmentCornerRadius > 0) { mSegmentCornerRadius = scaleFromDensity( mSegmentCornerRadius, sourceDensity, targetDensity); } if (mPointRadius > 0) { mPointRadius = scaleFromDensity( Loading Loading @@ -788,17 +747,9 @@ public final class NotificationProgressDrawable extends Drawable { } } public void setStroke(int width, int color, float dashWidth, float dashGap) { mStrokeWidth = width; mStrokeDashWidth = dashWidth; mStrokeDashGap = dashGap; setStrokeColor(color); } public void setStrokeColor(int color) { mStrokeColor = color; mFadedStrokeColor = getFadedColor(color); public void setSegmentColor(int color) { mSegmentColor = color; mFadedSegmentColor = getFadedColor(color); } public void setPointRectColor(int color) { Loading @@ -808,11 +759,14 @@ public final class NotificationProgressDrawable extends Drawable { } /** * Get a color with an opacity that's 50% of the input color. * Get a color with an opacity that's 25% of the input color. */ @ColorInt static int getFadedColor(@ColorInt int color) { return Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color), return Color.argb( (int) (Color.alpha(color) * 0.25f + 0.5f), Color.red(color), Color.green(color), Color.blue(color)); } Loading @@ -836,15 +790,6 @@ public final class NotificationProgressDrawable extends Drawable { } private void updateLocalState() { final State state = mState; mStrokePaint.setStrokeWidth(state.mStrokeWidth); mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth); if (state.mStrokeDashWidth != 0.0f) { final DashPathEffect e = new DashPathEffect( new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0); mDashedStrokePaint.setPathEffect(e); } // NO-OP } }
core/res/res/drawable/notification_progress.xml +3 −3 Original line number Diff line number Diff line Loading @@ -24,9 +24,9 @@ android:segPointGap="@dimen/notification_progress_segPoint_gap"> <segments android:color="?attr/colorProgressBackgroundNormal" android:dashGap="@dimen/notification_progress_segments_dash_gap" android:dashWidth="@dimen/notification_progress_segments_dash_width" android:width="@dimen/notification_progress_segments_height" /> android:height="@dimen/notification_progress_segments_height" android:fadedHeight="@dimen/notification_progress_segments_faded_height" android:cornerRadius="@dimen/notification_progress_segments_corner_radius"/> <points android:color="?attr/colorProgressBackgroundNormal" android:radius="@dimen/notification_progress_points_radius" Loading
core/res/res/values/attrs.xml +8 −8 Original line number Diff line number Diff line Loading @@ -7748,14 +7748,14 @@ <!-- Used to config the segments of a NotificationProgressDrawable. --> <!-- @hide internal use only --> <declare-styleable name="NotificationProgressDrawableSegments"> <!-- Width of the stroke. --> <attr name="width" /> <!-- Default color of the stroke. --> <!-- Height of the solid segments --> <attr name="height" /> <!-- Height of the faded segments --> <attr name="fadedHeight" format="dimension"/> <!-- Corner radius of the segment rect. --> <attr name="cornerRadius" format="dimension" /> <!-- Default color of the segment. --> <attr name="color" /> <!-- Length of a dash in the stroke for the dashed segments. --> <attr name="dashWidth" /> <!-- Gap between dashes in the stroke for the dashed segments. --> <attr name="dashGap" /> </declare-styleable> <!-- Used to config the points of a NotificationProgressDrawable. --> Loading @@ -7766,7 +7766,7 @@ <!-- Inset of the point icon or rect. --> <attr name="inset" /> <!-- Corner radius of the point rect. --> <attr name="cornerRadius" format="dimension" /> <attr name="cornerRadius"/> <!-- Default color of the point rect. --> <attr name="color" /> </declare-styleable> Loading
core/res/res/values/dimens.xml +5 −5 Original line number Diff line number Diff line Loading @@ -838,13 +838,13 @@ <!-- The gap between segments in the notification progress bar --> <dimen name="notification_progress_segSeg_gap">2dp</dimen> <!-- The gap between a segment and a point in the notification progress bar --> <dimen name="notification_progress_segPoint_gap">8dp</dimen> <!-- The dash gap of the notification progress bar segments --> <dimen name="notification_progress_segments_dash_gap">8dp</dimen> <!-- The dash width of the notification progress bar segments --> <dimen name="notification_progress_segments_dash_width">3dp</dimen> <dimen name="notification_progress_segPoint_gap">4dp</dimen> <!-- The height of the notification progress bar segments --> <dimen name="notification_progress_segments_height">6dp</dimen> <!-- The height of the notification progress bar faded segments --> <dimen name="notification_progress_segments_faded_height">2dp</dimen> <!-- The corner radius of the notification progress bar segments --> <dimen name="notification_progress_segments_corner_radius">16dp</dimen> <!-- The radius of the notification progress bar points --> <dimen name="notification_progress_points_radius">6dp</dimen> <!-- The corner radius of the notification progress bar points drawn as rects --> Loading