Loading core/java/android/app/Notification.java +25 −2 Original line number Diff line number Diff line Loading @@ -11854,7 +11854,7 @@ public class Notification implements Parcelable // If segment limit is exceeded. All segments will be replaced // with a single segment boolean allSameColor = true; int firstSegmentColor = segments.get(0).getColor(); int firstSegmentColor = segments.getFirst().getColor(); for (int i = 1; i < segments.size(); i++) { if (segments.get(i).getColor() != firstSegmentColor) { Loading Loading @@ -11887,8 +11887,31 @@ public class Notification implements Parcelable } } // If the segments and points can't all fit inside the progress drawable, the // view will replace all segments with a single segment. final int segmentsFallbackColor; if (segments.size() <= 1) { segmentsFallbackColor = NotificationProgressModel.INVALID_COLOR; } else { boolean allSameColor = true; int firstSegmentColor = segments.getFirst().getColor(); for (int i = 1; i < segments.size(); i++) { if (segments.get(i).getColor() != firstSegmentColor) { allSameColor = false; break; } } // If the segments are of the same color, the view can just use that color. // In that case there is no need to send the fallback color. segmentsFallbackColor = allSameColor ? NotificationProgressModel.INVALID_COLOR : sanitizeProgressColor(Notification.COLOR_DEFAULT, backgroundColor, defaultProgressColor); } model = new NotificationProgressModel(segments, points, Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress); Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress, segmentsFallbackColor); } return model; } Loading core/java/com/android/internal/widget/NotificationProgressBar.java +146 −34 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.internal.widget.NotificationProgressDrawable.DrawablePoint; import com.android.internal.widget.NotificationProgressDrawable.DrawableSegment; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; Loading Loading @@ -161,7 +162,7 @@ public final class NotificationProgressBar extends ProgressBar implements final int progress = mProgressModel.getProgress(); final int progressMax = mProgressModel.getProgressMax(); mParts = processAndConvertToViewParts(mProgressModel.getSegments(), mParts = processModelAndConvertToViewParts(mProgressModel.getSegments(), mProgressModel.getPoints(), progress, progressMax); Loading Loading @@ -439,23 +440,107 @@ public final class NotificationProgressBar extends ProgressBar implements return; } mProgressDrawableParts = processAndConvertToDrawableParts( final float segSegGap = mNotificationProgressDrawable.getSegSegGap(); final float segPointGap = mNotificationProgressDrawable.getSegPointGap(); final float pointRadius = mNotificationProgressDrawable.getPointRadius(); mProgressDrawableParts = processPartsAndConvertToDrawableParts( mParts, width, mNotificationProgressDrawable.getSegSegGap(), mNotificationProgressDrawable.getSegPointGap(), mNotificationProgressDrawable.getPointRadius(), segSegGap, segPointGap, pointRadius, mHasTrackerIcon ); Pair<List<DrawablePart>, Float> p = maybeStretchAndRescaleSegments( final float segmentMinWidth = mNotificationProgressDrawable.getSegmentMinWidth(); final float progressFraction = getProgressFraction(); final boolean isStyledByProgress = mProgressModel.isStyledByProgress(); final float progressGap = mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap(); Pair<List<DrawablePart>, Float> p = null; try { p = maybeStretchAndRescaleSegments( mParts, mProgressDrawableParts, mNotificationProgressDrawable.getSegmentMinWidth(), mNotificationProgressDrawable.getPointRadius(), getProgressFraction(), segmentMinWidth, pointRadius, progressFraction, width, mProgressModel.isStyledByProgress(), mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap()); isStyledByProgress, progressGap ); } catch (NotEnoughWidthToFitAllPartsException ex) { Log.w(TAG, "Failed to stretch and rescale segments", ex); } List<ProgressStyle.Segment> fallbackSegments = null; if (p == null && mProgressModel.getSegments().size() > 1) { Log.w(TAG, "Falling back to single segment"); try { fallbackSegments = List.of(new ProgressStyle.Segment(getMax()).setColor( mProgressModel.getSegmentsFallbackColor() == NotificationProgressModel.INVALID_COLOR ? mProgressModel.getSegments().getFirst().getColor() : mProgressModel.getSegmentsFallbackColor())); p = processModelAndConvertToFinalDrawableParts( fallbackSegments, mProgressModel.getPoints(), mProgressModel.getProgress(), getMax(), width, segSegGap, segPointGap, pointRadius, mHasTrackerIcon, segmentMinWidth, isStyledByProgress ); } catch (NotEnoughWidthToFitAllPartsException ex) { Log.w(TAG, "Failed to stretch and rescale segments with single segment fallback", ex); } } if (p == null && !mProgressModel.getPoints().isEmpty()) { Log.w(TAG, "Falling back to single segment and no points"); if (fallbackSegments == null) { fallbackSegments = List.of(new ProgressStyle.Segment(getMax()).setColor( mProgressModel.getSegmentsFallbackColor() == NotificationProgressModel.INVALID_COLOR ? mProgressModel.getSegments().getFirst().getColor() : mProgressModel.getSegmentsFallbackColor())); } try { p = processModelAndConvertToFinalDrawableParts( fallbackSegments, Collections.emptyList(), mProgressModel.getProgress(), getMax(), width, segSegGap, segPointGap, pointRadius, mHasTrackerIcon, segmentMinWidth, isStyledByProgress ); } catch (NotEnoughWidthToFitAllPartsException ex) { Log.w(TAG, "Failed to stretch and rescale segments with single segments and no points", ex); } } if (p == null) { Log.w(TAG, "Falling back to no stretching and rescaling"); p = maybeSplitDrawableSegmentsByProgress( mParts, mProgressDrawableParts, progressFraction, width, isStyledByProgress, progressGap); } if (DEBUG) { Log.d(TAG, "Updating NotificationProgressDrawable parts"); Loading Loading @@ -502,7 +587,11 @@ public final class NotificationProgressBar extends ProgressBar implements int min = getMin(); int max = getMax(); int range = max - min; return range > 0 ? (getProgress() - min) / (float) range : 0; return getProgressFraction(range, (getProgress() - min)); } private static float getProgressFraction(int progressMax, int progress) { return progressMax > 0 ? progress / (float) progressMax : 0; } /** Loading Loading @@ -636,7 +725,7 @@ public final class NotificationProgressBar extends ProgressBar implements * Processes the ProgressStyle data and convert to a list of {@code Part}. */ @VisibleForTesting public static List<Part> processAndConvertToViewParts( public static List<Part> processModelAndConvertToViewParts( List<ProgressStyle.Segment> segments, List<ProgressStyle.Point> points, int progress, Loading Loading @@ -796,7 +885,7 @@ public final class NotificationProgressBar extends ProgressBar implements * Processes the list of {@code Part} and convert to a list of {@code DrawablePart}. */ @VisibleForTesting public static List<DrawablePart> processAndConvertToDrawableParts( public static List<DrawablePart> processPartsAndConvertToDrawableParts( List<Part> parts, float totalWidth, float segSegGap, Loading @@ -823,7 +912,7 @@ public final class NotificationProgressBar extends ProgressBar implements // Retract the end position to account for the padding and a point immediately // after. final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap, segSegGap, iPart == nParts - 2, totalWidth, hasTrackerIcon); segSegGap, iPart == nParts - 2, hasTrackerIcon); final float end = x + segWidth - endOffset; drawableParts.add(new DrawableSegment(start, end, segment.mColor, segment.mFaded)); Loading Loading @@ -864,7 +953,7 @@ public final class NotificationProgressBar extends ProgressBar implements } private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius, float segPointGap, float segSegGap, boolean isSecondToLastPart, float totalWidth, float segPointGap, float segSegGap, boolean isSecondToLastPart, boolean hasTrackerIcon) { if (nextPart == null) return 0F; if (nextPart instanceof Segment nextSeg) { Loading Loading @@ -894,7 +983,7 @@ public final class NotificationProgressBar extends ProgressBar implements float totalWidth, boolean isStyledByProgress, float progressGap ) { ) throws NotEnoughWidthToFitAllPartsException { final List<DrawableSegment> drawableSegments = drawableParts .stream() .filter(DrawableSegment.class::isInstance) Loading @@ -920,16 +1009,8 @@ public final class NotificationProgressBar extends ProgressBar implements } if (totalExcessWidth < 0) { // TODO: b/372908709 - throw an error so that the caller can catch and go to fallback // option. (instead of return.) Log.w(TAG, "Not enough width to satisfy the minimum width for segments."); return maybeSplitDrawableSegmentsByProgress( parts, drawableParts, progressFraction, totalWidth, isStyledByProgress, progressGap); throw new NotEnoughWidthToFitAllPartsException( "Not enough width to satisfy the minimum width for segments."); } final int nParts = drawableParts.size(); Loading Loading @@ -1003,8 +1084,7 @@ public final class NotificationProgressBar extends ProgressBar implements final int nParts = parts.size(); for (int iPart = 0; iPart < nParts; iPart++) { final Part part = parts.get(iPart); if (!(part instanceof Segment)) continue; final Segment segment = (Segment) part; if (!(part instanceof Segment segment)) continue; if (startFraction == progressFraction) { iPartFirstSegmentToStyle = iPart; rescaledProgressX = segment.mStart; Loading Loading @@ -1065,12 +1145,38 @@ public final class NotificationProgressBar extends ProgressBar implements return new Pair<>(splitDrawableParts, rescaledProgressX); } /** * Processes the ProgressStyle data and convert to a pair of: * - list of processed {@code DrawablePart}. * - location of progress on the stretched and rescaled progress bar. */ @VisibleForTesting public static Pair<List<DrawablePart>, Float> processModelAndConvertToFinalDrawableParts( List<ProgressStyle.Segment> segments, List<ProgressStyle.Point> points, int progress, int progressMax, float totalWidth, float segSegGap, float segPointGap, float pointRadius, boolean hasTrackerIcon, float segmentMinWidth, boolean isStyledByProgress ) throws NotEnoughWidthToFitAllPartsException { List<Part> parts = processModelAndConvertToViewParts(segments, points, progress, progressMax); List<DrawablePart> drawableParts = processPartsAndConvertToDrawableParts(parts, totalWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon); return maybeStretchAndRescaleSegments(parts, drawableParts, segmentMinWidth, pointRadius, getProgressFraction(progressMax, progress), totalWidth, isStyledByProgress, hasTrackerIcon ? 0F : segSegGap); } /** * A part of the progress bar, which is either a {@link Segment} with non-zero length, or a * {@link Point} with zero length. */ // TODO: b/372908709 - maybe this should be made private? Only test the final // NotificationDrawable.Parts. public interface Part { } Loading Loading @@ -1176,4 +1282,10 @@ public final class NotificationProgressBar extends ProgressBar implements return Objects.hash(mColor); } } public static class NotEnoughWidthToFitAllPartsException extends Exception { public NotEnoughWidthToFitAllPartsException(String message) { super(message); } } } core/java/com/android/internal/widget/NotificationProgressModel.java +30 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.internal.widget; import android.annotation.ColorInt; import android.annotation.FlaggedApi; import android.annotation.NonNull; Loading Loading @@ -45,16 +44,20 @@ import java.util.Objects; */ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING) public final class NotificationProgressModel { private static final int INVALID_INDETERMINATE_COLOR = Color.TRANSPARENT; public static final int INVALID_COLOR = Color.TRANSPARENT; private static final String KEY_SEGMENTS = "segments"; private static final String KEY_POINTS = "points"; private static final String KEY_PROGRESS = "progress"; private static final String KEY_IS_STYLED_BY_PROGRESS = "isStyledByProgress"; private static final String KEY_SEGMENTS_FALLBACK_COLOR = "segmentsFallColor"; private static final String KEY_INDETERMINATE_COLOR = "indeterminateColor"; private final List<Segment> mSegments; private final List<Point> mPoints; private final int mProgress; private final boolean mIsStyledByProgress; @ColorInt private final int mSegmentsFallbackColor; @ColorInt private final int mIndeterminateColor; Loading @@ -62,7 +65,8 @@ public final class NotificationProgressModel { @NonNull List<Segment> segments, @NonNull List<Point> points, int progress, boolean isStyledByProgress boolean isStyledByProgress, @ColorInt int segmentsFallbackColor ) { Preconditions.checkArgument(progress >= 0); Preconditions.checkArgument(!segments.isEmpty()); Loading @@ -70,17 +74,19 @@ public final class NotificationProgressModel { mPoints = points; mProgress = progress; mIsStyledByProgress = isStyledByProgress; mIndeterminateColor = INVALID_INDETERMINATE_COLOR; mSegmentsFallbackColor = segmentsFallbackColor; mIndeterminateColor = INVALID_COLOR; } public NotificationProgressModel( @ColorInt int indeterminateColor ) { Preconditions.checkArgument(indeterminateColor != INVALID_INDETERMINATE_COLOR); Preconditions.checkArgument(indeterminateColor != INVALID_COLOR); mSegments = Collections.emptyList(); mPoints = Collections.emptyList(); mProgress = 0; mIsStyledByProgress = false; mSegmentsFallbackColor = INVALID_COLOR; mIndeterminateColor = indeterminateColor; } Loading @@ -104,13 +110,18 @@ public final class NotificationProgressModel { return mIsStyledByProgress; } @ColorInt public int getSegmentsFallbackColor() { return mSegmentsFallbackColor; } @ColorInt public int getIndeterminateColor() { return mIndeterminateColor; } public boolean isIndeterminate() { return mIndeterminateColor != INVALID_INDETERMINATE_COLOR; return mIndeterminateColor != INVALID_COLOR; } /** Loading @@ -119,7 +130,7 @@ public final class NotificationProgressModel { @NonNull public Bundle toBundle() { final Bundle bundle = new Bundle(); if (mIndeterminateColor != INVALID_INDETERMINATE_COLOR) { if (mIndeterminateColor != INVALID_COLOR) { bundle.putInt(KEY_INDETERMINATE_COLOR, mIndeterminateColor); } else { bundle.putParcelableList(KEY_SEGMENTS, Loading @@ -128,6 +139,9 @@ public final class NotificationProgressModel { Notification.ProgressStyle.getProgressPointsAsBundleList(mPoints)); bundle.putInt(KEY_PROGRESS, mProgress); bundle.putBoolean(KEY_IS_STYLED_BY_PROGRESS, mIsStyledByProgress); if (mSegmentsFallbackColor != INVALID_COLOR) { bundle.putInt(KEY_SEGMENTS_FALLBACK_COLOR, mSegmentsFallbackColor); } } return bundle; } Loading @@ -138,8 +152,8 @@ public final class NotificationProgressModel { @NonNull public static NotificationProgressModel fromBundle(@NonNull Bundle bundle) { final int indeterminateColor = bundle.getInt(KEY_INDETERMINATE_COLOR, INVALID_INDETERMINATE_COLOR); if (indeterminateColor != INVALID_INDETERMINATE_COLOR) { INVALID_COLOR); if (indeterminateColor != INVALID_COLOR) { return new NotificationProgressModel(indeterminateColor); } else { final List<Segment> segments = Loading @@ -150,7 +164,10 @@ public final class NotificationProgressModel { bundle.getParcelableArrayList(KEY_POINTS, Bundle.class)); final int progress = bundle.getInt(KEY_PROGRESS); final boolean isStyledByProgress = bundle.getBoolean(KEY_IS_STYLED_BY_PROGRESS); return new NotificationProgressModel(segments, points, progress, isStyledByProgress); final int segmentsFallbackColor = bundle.getInt(KEY_SEGMENTS_FALLBACK_COLOR, INVALID_COLOR); return new NotificationProgressModel(segments, points, progress, isStyledByProgress, segmentsFallbackColor); } } Loading @@ -161,6 +178,7 @@ public final class NotificationProgressModel { + ", mPoints=" + mPoints + ", mProgress=" + mProgress + ", mIsStyledByProgress=" + mIsStyledByProgress + ", mSegmentsFallbackColor=" + mSegmentsFallbackColor + ", mIndeterminateColor=" + mIndeterminateColor + "}"; } Loading @@ -171,6 +189,7 @@ public final class NotificationProgressModel { final NotificationProgressModel that = (NotificationProgressModel) o; return mProgress == that.mProgress && mIsStyledByProgress == that.mIsStyledByProgress && mSegmentsFallbackColor == that.mSegmentsFallbackColor && mIndeterminateColor == that.mIndeterminateColor && Objects.equals(mSegments, that.mSegments) && Objects.equals(mPoints, that.mPoints); Loading @@ -182,6 +201,7 @@ public final class NotificationProgressModel { mPoints, mProgress, mIsStyledByProgress, mSegmentsFallbackColor, mIndeterminateColor); } } core/res/res/values/attrs.xml +1 −1 Original line number Diff line number Diff line Loading @@ -7585,7 +7585,7 @@ <!-- Used to config the segments of a NotificationProgressDrawable. --> <!-- @hide internal use only --> <declare-styleable name="NotificationProgressDrawableSegments"> <!-- TODO: b/372908709 - maybe move this to NotificationProgressBar, because that's the only <!-- TODO: b/390196782 - maybe move this to NotificationProgressBar, because that's the only place this is used actually. Same for NotificationProgressDrawable.segSegGap/segPointGap above. --> <!-- Minimum required drawing width. The drawing width refers to the width after Loading core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java +312 −68 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/Notification.java +25 −2 Original line number Diff line number Diff line Loading @@ -11854,7 +11854,7 @@ public class Notification implements Parcelable // If segment limit is exceeded. All segments will be replaced // with a single segment boolean allSameColor = true; int firstSegmentColor = segments.get(0).getColor(); int firstSegmentColor = segments.getFirst().getColor(); for (int i = 1; i < segments.size(); i++) { if (segments.get(i).getColor() != firstSegmentColor) { Loading Loading @@ -11887,8 +11887,31 @@ public class Notification implements Parcelable } } // If the segments and points can't all fit inside the progress drawable, the // view will replace all segments with a single segment. final int segmentsFallbackColor; if (segments.size() <= 1) { segmentsFallbackColor = NotificationProgressModel.INVALID_COLOR; } else { boolean allSameColor = true; int firstSegmentColor = segments.getFirst().getColor(); for (int i = 1; i < segments.size(); i++) { if (segments.get(i).getColor() != firstSegmentColor) { allSameColor = false; break; } } // If the segments are of the same color, the view can just use that color. // In that case there is no need to send the fallback color. segmentsFallbackColor = allSameColor ? NotificationProgressModel.INVALID_COLOR : sanitizeProgressColor(Notification.COLOR_DEFAULT, backgroundColor, defaultProgressColor); } model = new NotificationProgressModel(segments, points, Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress); Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress, segmentsFallbackColor); } return model; } Loading
core/java/com/android/internal/widget/NotificationProgressBar.java +146 −34 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.internal.widget.NotificationProgressDrawable.DrawablePoint; import com.android.internal.widget.NotificationProgressDrawable.DrawableSegment; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; Loading Loading @@ -161,7 +162,7 @@ public final class NotificationProgressBar extends ProgressBar implements final int progress = mProgressModel.getProgress(); final int progressMax = mProgressModel.getProgressMax(); mParts = processAndConvertToViewParts(mProgressModel.getSegments(), mParts = processModelAndConvertToViewParts(mProgressModel.getSegments(), mProgressModel.getPoints(), progress, progressMax); Loading Loading @@ -439,23 +440,107 @@ public final class NotificationProgressBar extends ProgressBar implements return; } mProgressDrawableParts = processAndConvertToDrawableParts( final float segSegGap = mNotificationProgressDrawable.getSegSegGap(); final float segPointGap = mNotificationProgressDrawable.getSegPointGap(); final float pointRadius = mNotificationProgressDrawable.getPointRadius(); mProgressDrawableParts = processPartsAndConvertToDrawableParts( mParts, width, mNotificationProgressDrawable.getSegSegGap(), mNotificationProgressDrawable.getSegPointGap(), mNotificationProgressDrawable.getPointRadius(), segSegGap, segPointGap, pointRadius, mHasTrackerIcon ); Pair<List<DrawablePart>, Float> p = maybeStretchAndRescaleSegments( final float segmentMinWidth = mNotificationProgressDrawable.getSegmentMinWidth(); final float progressFraction = getProgressFraction(); final boolean isStyledByProgress = mProgressModel.isStyledByProgress(); final float progressGap = mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap(); Pair<List<DrawablePart>, Float> p = null; try { p = maybeStretchAndRescaleSegments( mParts, mProgressDrawableParts, mNotificationProgressDrawable.getSegmentMinWidth(), mNotificationProgressDrawable.getPointRadius(), getProgressFraction(), segmentMinWidth, pointRadius, progressFraction, width, mProgressModel.isStyledByProgress(), mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap()); isStyledByProgress, progressGap ); } catch (NotEnoughWidthToFitAllPartsException ex) { Log.w(TAG, "Failed to stretch and rescale segments", ex); } List<ProgressStyle.Segment> fallbackSegments = null; if (p == null && mProgressModel.getSegments().size() > 1) { Log.w(TAG, "Falling back to single segment"); try { fallbackSegments = List.of(new ProgressStyle.Segment(getMax()).setColor( mProgressModel.getSegmentsFallbackColor() == NotificationProgressModel.INVALID_COLOR ? mProgressModel.getSegments().getFirst().getColor() : mProgressModel.getSegmentsFallbackColor())); p = processModelAndConvertToFinalDrawableParts( fallbackSegments, mProgressModel.getPoints(), mProgressModel.getProgress(), getMax(), width, segSegGap, segPointGap, pointRadius, mHasTrackerIcon, segmentMinWidth, isStyledByProgress ); } catch (NotEnoughWidthToFitAllPartsException ex) { Log.w(TAG, "Failed to stretch and rescale segments with single segment fallback", ex); } } if (p == null && !mProgressModel.getPoints().isEmpty()) { Log.w(TAG, "Falling back to single segment and no points"); if (fallbackSegments == null) { fallbackSegments = List.of(new ProgressStyle.Segment(getMax()).setColor( mProgressModel.getSegmentsFallbackColor() == NotificationProgressModel.INVALID_COLOR ? mProgressModel.getSegments().getFirst().getColor() : mProgressModel.getSegmentsFallbackColor())); } try { p = processModelAndConvertToFinalDrawableParts( fallbackSegments, Collections.emptyList(), mProgressModel.getProgress(), getMax(), width, segSegGap, segPointGap, pointRadius, mHasTrackerIcon, segmentMinWidth, isStyledByProgress ); } catch (NotEnoughWidthToFitAllPartsException ex) { Log.w(TAG, "Failed to stretch and rescale segments with single segments and no points", ex); } } if (p == null) { Log.w(TAG, "Falling back to no stretching and rescaling"); p = maybeSplitDrawableSegmentsByProgress( mParts, mProgressDrawableParts, progressFraction, width, isStyledByProgress, progressGap); } if (DEBUG) { Log.d(TAG, "Updating NotificationProgressDrawable parts"); Loading Loading @@ -502,7 +587,11 @@ public final class NotificationProgressBar extends ProgressBar implements int min = getMin(); int max = getMax(); int range = max - min; return range > 0 ? (getProgress() - min) / (float) range : 0; return getProgressFraction(range, (getProgress() - min)); } private static float getProgressFraction(int progressMax, int progress) { return progressMax > 0 ? progress / (float) progressMax : 0; } /** Loading Loading @@ -636,7 +725,7 @@ public final class NotificationProgressBar extends ProgressBar implements * Processes the ProgressStyle data and convert to a list of {@code Part}. */ @VisibleForTesting public static List<Part> processAndConvertToViewParts( public static List<Part> processModelAndConvertToViewParts( List<ProgressStyle.Segment> segments, List<ProgressStyle.Point> points, int progress, Loading Loading @@ -796,7 +885,7 @@ public final class NotificationProgressBar extends ProgressBar implements * Processes the list of {@code Part} and convert to a list of {@code DrawablePart}. */ @VisibleForTesting public static List<DrawablePart> processAndConvertToDrawableParts( public static List<DrawablePart> processPartsAndConvertToDrawableParts( List<Part> parts, float totalWidth, float segSegGap, Loading @@ -823,7 +912,7 @@ public final class NotificationProgressBar extends ProgressBar implements // Retract the end position to account for the padding and a point immediately // after. final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap, segSegGap, iPart == nParts - 2, totalWidth, hasTrackerIcon); segSegGap, iPart == nParts - 2, hasTrackerIcon); final float end = x + segWidth - endOffset; drawableParts.add(new DrawableSegment(start, end, segment.mColor, segment.mFaded)); Loading Loading @@ -864,7 +953,7 @@ public final class NotificationProgressBar extends ProgressBar implements } private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius, float segPointGap, float segSegGap, boolean isSecondToLastPart, float totalWidth, float segPointGap, float segSegGap, boolean isSecondToLastPart, boolean hasTrackerIcon) { if (nextPart == null) return 0F; if (nextPart instanceof Segment nextSeg) { Loading Loading @@ -894,7 +983,7 @@ public final class NotificationProgressBar extends ProgressBar implements float totalWidth, boolean isStyledByProgress, float progressGap ) { ) throws NotEnoughWidthToFitAllPartsException { final List<DrawableSegment> drawableSegments = drawableParts .stream() .filter(DrawableSegment.class::isInstance) Loading @@ -920,16 +1009,8 @@ public final class NotificationProgressBar extends ProgressBar implements } if (totalExcessWidth < 0) { // TODO: b/372908709 - throw an error so that the caller can catch and go to fallback // option. (instead of return.) Log.w(TAG, "Not enough width to satisfy the minimum width for segments."); return maybeSplitDrawableSegmentsByProgress( parts, drawableParts, progressFraction, totalWidth, isStyledByProgress, progressGap); throw new NotEnoughWidthToFitAllPartsException( "Not enough width to satisfy the minimum width for segments."); } final int nParts = drawableParts.size(); Loading Loading @@ -1003,8 +1084,7 @@ public final class NotificationProgressBar extends ProgressBar implements final int nParts = parts.size(); for (int iPart = 0; iPart < nParts; iPart++) { final Part part = parts.get(iPart); if (!(part instanceof Segment)) continue; final Segment segment = (Segment) part; if (!(part instanceof Segment segment)) continue; if (startFraction == progressFraction) { iPartFirstSegmentToStyle = iPart; rescaledProgressX = segment.mStart; Loading Loading @@ -1065,12 +1145,38 @@ public final class NotificationProgressBar extends ProgressBar implements return new Pair<>(splitDrawableParts, rescaledProgressX); } /** * Processes the ProgressStyle data and convert to a pair of: * - list of processed {@code DrawablePart}. * - location of progress on the stretched and rescaled progress bar. */ @VisibleForTesting public static Pair<List<DrawablePart>, Float> processModelAndConvertToFinalDrawableParts( List<ProgressStyle.Segment> segments, List<ProgressStyle.Point> points, int progress, int progressMax, float totalWidth, float segSegGap, float segPointGap, float pointRadius, boolean hasTrackerIcon, float segmentMinWidth, boolean isStyledByProgress ) throws NotEnoughWidthToFitAllPartsException { List<Part> parts = processModelAndConvertToViewParts(segments, points, progress, progressMax); List<DrawablePart> drawableParts = processPartsAndConvertToDrawableParts(parts, totalWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon); return maybeStretchAndRescaleSegments(parts, drawableParts, segmentMinWidth, pointRadius, getProgressFraction(progressMax, progress), totalWidth, isStyledByProgress, hasTrackerIcon ? 0F : segSegGap); } /** * A part of the progress bar, which is either a {@link Segment} with non-zero length, or a * {@link Point} with zero length. */ // TODO: b/372908709 - maybe this should be made private? Only test the final // NotificationDrawable.Parts. public interface Part { } Loading Loading @@ -1176,4 +1282,10 @@ public final class NotificationProgressBar extends ProgressBar implements return Objects.hash(mColor); } } public static class NotEnoughWidthToFitAllPartsException extends Exception { public NotEnoughWidthToFitAllPartsException(String message) { super(message); } } }
core/java/com/android/internal/widget/NotificationProgressModel.java +30 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.internal.widget; import android.annotation.ColorInt; import android.annotation.FlaggedApi; import android.annotation.NonNull; Loading Loading @@ -45,16 +44,20 @@ import java.util.Objects; */ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING) public final class NotificationProgressModel { private static final int INVALID_INDETERMINATE_COLOR = Color.TRANSPARENT; public static final int INVALID_COLOR = Color.TRANSPARENT; private static final String KEY_SEGMENTS = "segments"; private static final String KEY_POINTS = "points"; private static final String KEY_PROGRESS = "progress"; private static final String KEY_IS_STYLED_BY_PROGRESS = "isStyledByProgress"; private static final String KEY_SEGMENTS_FALLBACK_COLOR = "segmentsFallColor"; private static final String KEY_INDETERMINATE_COLOR = "indeterminateColor"; private final List<Segment> mSegments; private final List<Point> mPoints; private final int mProgress; private final boolean mIsStyledByProgress; @ColorInt private final int mSegmentsFallbackColor; @ColorInt private final int mIndeterminateColor; Loading @@ -62,7 +65,8 @@ public final class NotificationProgressModel { @NonNull List<Segment> segments, @NonNull List<Point> points, int progress, boolean isStyledByProgress boolean isStyledByProgress, @ColorInt int segmentsFallbackColor ) { Preconditions.checkArgument(progress >= 0); Preconditions.checkArgument(!segments.isEmpty()); Loading @@ -70,17 +74,19 @@ public final class NotificationProgressModel { mPoints = points; mProgress = progress; mIsStyledByProgress = isStyledByProgress; mIndeterminateColor = INVALID_INDETERMINATE_COLOR; mSegmentsFallbackColor = segmentsFallbackColor; mIndeterminateColor = INVALID_COLOR; } public NotificationProgressModel( @ColorInt int indeterminateColor ) { Preconditions.checkArgument(indeterminateColor != INVALID_INDETERMINATE_COLOR); Preconditions.checkArgument(indeterminateColor != INVALID_COLOR); mSegments = Collections.emptyList(); mPoints = Collections.emptyList(); mProgress = 0; mIsStyledByProgress = false; mSegmentsFallbackColor = INVALID_COLOR; mIndeterminateColor = indeterminateColor; } Loading @@ -104,13 +110,18 @@ public final class NotificationProgressModel { return mIsStyledByProgress; } @ColorInt public int getSegmentsFallbackColor() { return mSegmentsFallbackColor; } @ColorInt public int getIndeterminateColor() { return mIndeterminateColor; } public boolean isIndeterminate() { return mIndeterminateColor != INVALID_INDETERMINATE_COLOR; return mIndeterminateColor != INVALID_COLOR; } /** Loading @@ -119,7 +130,7 @@ public final class NotificationProgressModel { @NonNull public Bundle toBundle() { final Bundle bundle = new Bundle(); if (mIndeterminateColor != INVALID_INDETERMINATE_COLOR) { if (mIndeterminateColor != INVALID_COLOR) { bundle.putInt(KEY_INDETERMINATE_COLOR, mIndeterminateColor); } else { bundle.putParcelableList(KEY_SEGMENTS, Loading @@ -128,6 +139,9 @@ public final class NotificationProgressModel { Notification.ProgressStyle.getProgressPointsAsBundleList(mPoints)); bundle.putInt(KEY_PROGRESS, mProgress); bundle.putBoolean(KEY_IS_STYLED_BY_PROGRESS, mIsStyledByProgress); if (mSegmentsFallbackColor != INVALID_COLOR) { bundle.putInt(KEY_SEGMENTS_FALLBACK_COLOR, mSegmentsFallbackColor); } } return bundle; } Loading @@ -138,8 +152,8 @@ public final class NotificationProgressModel { @NonNull public static NotificationProgressModel fromBundle(@NonNull Bundle bundle) { final int indeterminateColor = bundle.getInt(KEY_INDETERMINATE_COLOR, INVALID_INDETERMINATE_COLOR); if (indeterminateColor != INVALID_INDETERMINATE_COLOR) { INVALID_COLOR); if (indeterminateColor != INVALID_COLOR) { return new NotificationProgressModel(indeterminateColor); } else { final List<Segment> segments = Loading @@ -150,7 +164,10 @@ public final class NotificationProgressModel { bundle.getParcelableArrayList(KEY_POINTS, Bundle.class)); final int progress = bundle.getInt(KEY_PROGRESS); final boolean isStyledByProgress = bundle.getBoolean(KEY_IS_STYLED_BY_PROGRESS); return new NotificationProgressModel(segments, points, progress, isStyledByProgress); final int segmentsFallbackColor = bundle.getInt(KEY_SEGMENTS_FALLBACK_COLOR, INVALID_COLOR); return new NotificationProgressModel(segments, points, progress, isStyledByProgress, segmentsFallbackColor); } } Loading @@ -161,6 +178,7 @@ public final class NotificationProgressModel { + ", mPoints=" + mPoints + ", mProgress=" + mProgress + ", mIsStyledByProgress=" + mIsStyledByProgress + ", mSegmentsFallbackColor=" + mSegmentsFallbackColor + ", mIndeterminateColor=" + mIndeterminateColor + "}"; } Loading @@ -171,6 +189,7 @@ public final class NotificationProgressModel { final NotificationProgressModel that = (NotificationProgressModel) o; return mProgress == that.mProgress && mIsStyledByProgress == that.mIsStyledByProgress && mSegmentsFallbackColor == that.mSegmentsFallbackColor && mIndeterminateColor == that.mIndeterminateColor && Objects.equals(mSegments, that.mSegments) && Objects.equals(mPoints, that.mPoints); Loading @@ -182,6 +201,7 @@ public final class NotificationProgressModel { mPoints, mProgress, mIsStyledByProgress, mSegmentsFallbackColor, mIndeterminateColor); } }
core/res/res/values/attrs.xml +1 −1 Original line number Diff line number Diff line Loading @@ -7585,7 +7585,7 @@ <!-- Used to config the segments of a NotificationProgressDrawable. --> <!-- @hide internal use only --> <declare-styleable name="NotificationProgressDrawableSegments"> <!-- TODO: b/372908709 - maybe move this to NotificationProgressBar, because that's the only <!-- TODO: b/390196782 - maybe move this to NotificationProgressBar, because that's the only place this is used actually. Same for NotificationProgressDrawable.segSegGap/segPointGap above. --> <!-- Minimum required drawing width. The drawing width refers to the width after Loading
core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java +312 −68 File changed.Preview size limit exceeded, changes collapsed. Show changes