Use NotificationProgressDrawable for ProgressStyle notifications.

Also tint the indeterminate drawable with the color passed from the API.

Fixed a couple of bugs in NotificationProgressDrawable, caught by the
tests.

Flag: android.app.api_rich_ongoing
Bug: 367805202
Fix: 372907485
Test: post a ProgressStyle Notification
Test: patch ag/29675286 and run ProgressStyleNotificationScreenshotTest
Change-Id: I2449f755fa359278351986f17b8744f6e2bc5827
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 3e597d7..d3b1f97 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -20,16 +20,20 @@
 import android.annotation.Nullable;
 import android.app.Notification.ProgressStyle;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.graphics.drawable.LayerDrawable;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.RemotableViewMethod;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
 
 import androidx.annotation.ColorInt;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.NotificationProgressDrawable.Part;
@@ -49,7 +53,13 @@
  */
 @RemoteViews.RemoteView
 public class NotificationProgressBar extends ProgressBar {
+    private static final String TAG = "NotificationProgressBar";
+
     private NotificationProgressModel mProgressModel;
+
+    @Nullable
+    private List<Part> mProgressDrawableParts = null;
+
     @Nullable
     private Drawable mProgressTrackerDrawable = null;
 
@@ -58,7 +68,7 @@
     }
 
     public NotificationProgressBar(Context context, AttributeSet attrs) {
-        this(context, attrs, com.android.internal.R.attr.progressBarStyle);
+        this(context, attrs, R.attr.progressBarStyle);
     }
 
     public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -82,10 +92,42 @@
                 "Bundle shouldn't be null");
 
         mProgressModel = NotificationProgressModel.fromBundle(bundle);
+
+        if (mProgressModel.isIndeterminate()) {
+            final int indeterminateColor = mProgressModel.getIndeterminateColor();
+            setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
+        } else {
+            mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
+                    mProgressModel.getPoints(),
+                    mProgressModel.getProgress(), 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);
+            }
+        }
     }
 
-    private void setProgressModel(@NonNull NotificationProgressModel model) {
-        mProgressModel = model;
+    @NonNull
+    private NotificationProgressDrawable getNotificationProgressDrawable() {
+        final Drawable d = getProgressDrawable();
+        if (d == null) {
+            throw new IllegalStateException("getProgressDrawable() returns null");
+        }
+        if (!(d instanceof LayerDrawable)) {
+            throw new IllegalStateException("getProgressDrawable() doesn't return a LayerDrawable");
+        }
+
+        final Drawable layer = ((LayerDrawable) d).findDrawableByLayerId(R.id.background);
+        if (!(layer instanceof NotificationProgressDrawable)) {
+            throw new IllegalStateException(
+                    "Couldn't get NotificationProgressDrawable, retrieved drawable is: " + (
+                            layer != null ? layer.toString() : null));
+        }
+
+        return (NotificationProgressDrawable) layer;
     }
 
     /**
@@ -97,7 +139,6 @@
     public void setProgressTrackerIcon(@Nullable Icon icon) {
     }
 
-
     /**
      * Async version of {@link #setProgressTrackerIcon}
      */
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 89ef875..2be7273 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -45,6 +45,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -156,11 +157,18 @@
     }
 
     /**
-     *
+     * Set the segments and points that constitute the drawable.
+     */
+    public void setParts(List<Part> parts) {
+        mParts.clear();
+        mParts.addAll(parts);
+    }
+
+    /**
+     * Set the segments and points that constitute the drawable.
      */
     public void setParts(@NonNull Part... parts) {
-        mParts.clear();
-        mParts.addAll(Arrays.asList(parts));
+        setParts(Arrays.asList(parts));
     }
 
     @Override
@@ -379,7 +387,7 @@
         if (state.mThemeAttrsPoints != null) {
             final TypedArray a = t.resolveAttributes(
                     state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints);
-            updateSegmentsFromTypedArray(a);
+            updatePointsFromTypedArray(a);
             a.recycle();
         }
     }
@@ -651,9 +659,11 @@
 
         State(@NonNull State orig, @Nullable Resources res) {
             mChangingConfigurations = orig.mChangingConfigurations;
+            mSegSegGap = orig.mSegSegGap;
+            mSegPointGap = orig.mSegPointGap;
+            mStrokeWidth = orig.mStrokeWidth;
             mStrokeColor = orig.mStrokeColor;
             mFadedStrokeColor = orig.mFadedStrokeColor;
-            mStrokeWidth = orig.mStrokeWidth;
             mStrokeDashWidth = orig.mStrokeDashWidth;
             mStrokeDashGap = orig.mStrokeDashGap;
             mPointRadius = orig.mPointRadius;
@@ -791,6 +801,7 @@
         final State state = mState;
 
         mStrokePaint.setStrokeWidth(state.mStrokeWidth);
+        mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth);
 
         if (state.mStrokeDashWidth != 0.0f) {
             final DashPathEffect e = new DashPathEffect(
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
new file mode 100644
index 0000000..3a6b600
--- /dev/null
+++ b/core/res/res/drawable/notification_progress.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@id/background"
+          android:gravity="center_vertical|fill_horizontal">
+        <com.android.internal.widget.NotificationProgressDrawable
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:segSegGap="@dimen/notification_progress_segSeg_gap"
+            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" />
+            <points
+                android:color="?attr/colorProgressBackgroundNormal"
+                android:radius="@dimen/notification_progress_points_radius"
+                android:cornerRadius="@dimen/notification_progress_points_corner_radius"
+                android:inset="@dimen/notification_progress_points_inset" />
+        </com.android.internal.widget.NotificationProgressDrawable>
+    </item>
+</layer-list>
diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml
index fdcefcc..75827a2 100644
--- a/core/res/res/layout/notification_template_material_progress.xml
+++ b/core/res/res/layout/notification_template_material_progress.xml
@@ -75,12 +75,11 @@
                         />
 
 
-                    <com.android.internal.widget.NotificationProgressBar
-                        android:id="@+id/progress"
+                    <include
                         android:layout_width="0dp"
-                        android:layout_height="@dimen/notification_progress_bar_height"
-                        style="@style/Widget.Material.Light.ProgressBar.Horizontal"
                         android:layout_weight="1"
+                        android:layout_height="@dimen/notification_progress_tracker_height"
+                        layout="@layout/notification_template_notification_progress_bar"
                         />
 
                     <com.android.internal.widget.CachingIconView
diff --git a/core/res/res/layout/notification_template_notification_progress_bar.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml
new file mode 100644
index 0000000..3574896
--- /dev/null
+++ b/core/res/res/layout/notification_template_notification_progress_bar.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.internal.widget.NotificationProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/progress"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/notification_progress_tracker_height"
+    style="@style/Widget.Material.Notification.NotificationProgressBar"
+    />
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b92aa2f..7184d9a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -817,6 +817,22 @@
     <dimen name="notification_progress_tracker_width">40dp</dimen>
     <!-- The size of the progress tracker height -->
     <dimen name="notification_progress_tracker_height">20dp</dimen>
+    <!-- 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">4dp</dimen>
+    <!-- The dash gap of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_dash_gap">9dp</dimen>
+    <!-- The dash width of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_dash_width">3dp</dimen>
+    <!-- The height of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_height">6dp</dimen>
+    <!-- The radius of the notification progress bar points -->
+    <dimen name="notification_progress_points_radius">10dp</dimen>
+    <!-- The corner radius of the notification progress bar points drawn as rects -->
+    <dimen name="notification_progress_points_corner_radius">4dp</dimen>
+    <!-- The inset of the notification progress bar points drawn as rects -->
+    <dimen name="notification_progress_points_inset">0dp</dimen>
 
     <!-- The maximum size of the small notification icon on low memory devices. -->
     <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index eec6ae3..cb8e4aa 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -502,6 +502,10 @@
 
     <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
 
+    <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
+        <item name="progressDrawable">@drawable/notification_progress</item>
+    </style>
+
     <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
         <item name="lineHeight">20sp</item>
         <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4f63fac..12b444b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3864,6 +3864,14 @@
   <java-symbol type="dimen" name="notification_progress_icon_size" />
   <java-symbol type="dimen" name="notification_progress_tracker_width" />
   <java-symbol type="dimen" name="notification_progress_tracker_height" />
+  <java-symbol type="dimen" name="notification_progress_segSeg_gap" />
+  <java-symbol type="dimen" name="notification_progress_segPoint_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_dash_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_dash_width" />
+  <java-symbol type="dimen" name="notification_progress_segments_height" />
+  <java-symbol type="dimen" name="notification_progress_points_radius" />
+  <java-symbol type="dimen" name="notification_progress_points_corner_radius" />
+  <java-symbol type="dimen" name="notification_progress_points_inset" />
 
   <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
   <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 6419c1e0..79a478a 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -254,7 +254,6 @@
 
         // Colors with 50% opacity
         int fadedGreen = 0x7F00FF00;
-        int fadedBlue = 0x7F0000FF;
         int fadedYellow = 0x7FFFFF00;
 
         List<Part> expected = new ArrayList<>(List.of(