Merge "Add API to let apps color CallStyle notifications themselves." into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 5eada4f..160ad7a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5610,6 +5610,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
     field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
     field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
@@ -5621,6 +5622,7 @@
     field public static final String EXTRA_COLORIZED = "android.colorized";
     field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
     field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
     field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
     field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
@@ -5908,6 +5910,8 @@
     method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
     method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent);
     method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+    method @NonNull public android.app.Notification.CallStyle setAnswerButtonColorHint(@ColorInt int);
+    method @NonNull public android.app.Notification.CallStyle setDeclineButtonColorHint(@ColorInt int);
     method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
     method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
   }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 899cdb5..f7304fb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1371,6 +1371,18 @@
     public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
 
     /**
+     * {@link #extras} key: the color used as a hint for the Answer action button of a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+     */
+    public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
+
+    /**
+     * {@link #extras} key: the color used as a hint for the Decline or Hang Up action button of a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+     */
+    public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
+
+    /**
      * {@link #extras} key: whether the notification should be colorized as
      * supplied to {@link Builder#setColorized(boolean)}.
      */
@@ -5444,6 +5456,11 @@
             return p.allowColorization && mN.isColorized();
         }
 
+        private boolean isCallActionColorCustomizable(StandardTemplateParams p) {
+            return isColorized(p) && mContext.getResources().getBoolean(
+                    R.bool.config_callNotificationActionColorsRequireColorized);
+        }
+
         private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
             if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
@@ -9066,6 +9083,8 @@
         private PendingIntent mAnswerIntent;
         private PendingIntent mDeclineIntent;
         private PendingIntent mHangUpIntent;
+        private Integer mAnswerButtonColor;
+        private Integer mDeclineButtonColor;
         private Icon mVerificationIcon;
         private CharSequence mVerificationText;
 
@@ -9176,6 +9195,28 @@
         }
 
         /**
+         * Optional color to be used as a hint for the Answer action button's color.
+         * The system may change this color to ensure sufficient contrast with the background.
+         * The system may choose to disregard this hint if the notification is not colorized.
+         */
+        @NonNull
+        public CallStyle setAnswerButtonColorHint(@ColorInt int color) {
+            mAnswerButtonColor = color;
+            return this;
+        }
+
+        /**
+         * Optional color to be used as a hint for the Decline or Hang Up action button's color.
+         * The system may change this color to ensure sufficient contrast with the background.
+         * The system may choose to disregard this hint if the notification is not colorized.
+         */
+        @NonNull
+        public CallStyle setDeclineButtonColorHint(@ColorInt int color) {
+            mDeclineButtonColor = color;
+            return this;
+        }
+
+        /**
          * @hide
          */
         public boolean displayCustomViewInline() {
@@ -9234,40 +9275,47 @@
         }
 
         @NonNull
-        private Action makeNegativeAction() {
+        private Action makeNegativeAction(@NonNull StandardTemplateParams p) {
             if (mDeclineIntent == null) {
-                return makeAction(R.drawable.ic_call_decline,
+                return makeAction(p, R.drawable.ic_call_decline,
                         R.string.call_notification_hang_up_action,
-                        R.color.call_notification_decline_color, mHangUpIntent);
+                        mDeclineButtonColor, R.color.call_notification_decline_color,
+                        mHangUpIntent);
             } else {
-                return makeAction(R.drawable.ic_call_decline,
+                return makeAction(p, R.drawable.ic_call_decline,
                         R.string.call_notification_decline_action,
-                        R.color.call_notification_decline_color, mDeclineIntent);
+                        mDeclineButtonColor, R.color.call_notification_decline_color,
+                        mDeclineIntent);
             }
         }
 
         @Nullable
-        private Action makeAnswerAction() {
-            return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
+        private Action makeAnswerAction(@NonNull StandardTemplateParams p) {
+            return mAnswerIntent == null ? null : makeAction(p, R.drawable.ic_call_answer,
                     R.string.call_notification_answer_action,
-                    R.color.call_notification_answer_color, mAnswerIntent);
+                    mAnswerButtonColor, R.color.call_notification_answer_color,
+                    mAnswerIntent);
         }
 
         @NonNull
-        private Action makeAction(@DrawableRes int icon, @StringRes int title,
-                @ColorRes int colorRes, PendingIntent intent) {
+        private Action makeAction(@NonNull StandardTemplateParams p,
+                @DrawableRes int icon, @StringRes int title,
+                @ColorInt Integer colorInt, @ColorRes int defaultColorRes, PendingIntent intent) {
+            if (colorInt == null || !mBuilder.isCallActionColorCustomizable(p)) {
+                colorInt = mBuilder.mContext.getColor(defaultColorRes);
+            }
             Action action = new Action.Builder(Icon.createWithResource("", icon),
                     new SpannableStringBuilder().append(mBuilder.mContext.getString(title),
-                            new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)),
+                            new ForegroundColorSpan(colorInt),
                             SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE),
                     intent).build();
             action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true);
             return action;
         }
 
-        private ArrayList<Action> makeActionsList() {
-            final Action negativeAction = makeNegativeAction();
-            final Action answerAction = makeAnswerAction();
+        private ArrayList<Action> makeActionsList(@NonNull StandardTemplateParams p) {
+            final Action negativeAction = makeNegativeAction(p);
+            final Action answerAction = makeAnswerAction(p);
 
             ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS);
             final Action lastAction;
@@ -9356,7 +9404,7 @@
 
             // Create the buttons for the generated actions list.
             int i = 0;
-            for (Action action : makeActionsList()) {
+            for (Action action : makeActionsList(p)) {
                 final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p);
                 if (i > 0) {
                     // Clear start margin from non-first buttons to reduce the gap between buttons.
@@ -9421,6 +9469,12 @@
             if (mHangUpIntent != null) {
                 extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent);
             }
+            if (mAnswerButtonColor != null) {
+                extras.putInt(EXTRA_ANSWER_COLOR, mAnswerButtonColor);
+            }
+            if (mDeclineButtonColor != null) {
+                extras.putInt(EXTRA_DECLINE_COLOR, mDeclineButtonColor);
+            }
             fixTitleAndTextExtras(extras);
         }
 
@@ -9447,6 +9501,10 @@
             mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT);
             mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT);
             mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT);
+            mAnswerButtonColor = extras.containsKey(EXTRA_ANSWER_COLOR)
+                    ? extras.getInt(EXTRA_ANSWER_COLOR) : null;
+            mDeclineButtonColor = extras.containsKey(EXTRA_DECLINE_COLOR)
+                    ? extras.getInt(EXTRA_DECLINE_COLOR) : null;
         }
 
         /**
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9a917b7..592a3a1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1173,6 +1173,9 @@
     <!-- Default value for LED off time when the battery is low on charge in miliseconds -->
     <integer name="config_notificationsBatteryLedOff">2875</integer>
 
+    <!-- If true, only colorized CallStyle notifications will apply custom colors -->
+    <bool name="config_callNotificationActionColorsRequireColorized">true</bool>
+
     <!-- Number of notifications to keep in the notification service historical archive -->
     <integer name="config_notificationServiceArchiveSize">100</integer>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b5af524..29b8e6e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2286,6 +2286,7 @@
   <java-symbol type="string" name="config_wifi_tether_enable" />
   <java-symbol type="bool" name="config_intrusiveNotificationLed" />
   <java-symbol type="bool" name="config_notificationBadging" />
+  <java-symbol type="bool" name="config_callNotificationActionColorsRequireColorized" />
   <java-symbol type="dimen" name="preference_fragment_padding_bottom" />
   <java-symbol type="dimen" name="preference_fragment_padding_side" />
   <java-symbol type="drawable" name="expander_ic_maximized" />