Loading packages/SystemUI/res/values/colors.xml +1 −0 Original line number Diff line number Diff line Loading @@ -148,6 +148,7 @@ <!-- Animated Action colors --> <color name="animated_action_button_text_color">@androidprv:color/materialColorOnSurface</color> <color name="animated_action_button_stroke_color">@androidprv:color/materialColorOnSurface</color> <color name="animated_action_button_attribution_color">@androidprv:color/materialColorOnSurfaceVariant</color> <!-- Biometric dialog colors --> <color name="biometric_dialog_gray">#ff757575</color> Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +73 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,10 @@ import android.graphics.drawable.Icon import android.os.Build import android.os.Bundle import android.os.SystemClock import android.text.Annotation import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.ForegroundColorSpan import android.util.Log import android.view.ContextThemeWrapper import android.view.LayoutInflater Loading Loading @@ -504,15 +508,40 @@ constructor( choice: CharSequence, delayOnClickListener: Boolean, ): Button { val layoutRes = val enableAnimatedReply = Flags.notificationAnimatedActionsTreatment() && smartReplies.fromAssistant && isAnimatedReply(choice) val layoutRes = if (enableAnimatedReply) { R.layout.animated_action_button } else { if (notificationsRedesignTemplates()) R.layout.notification_2025_smart_reply_button else R.layout.smart_reply_button } return (LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) as Button) .apply { // choiceToDeliver does not contain Annotation with extra data val choiceToDeliver: CharSequence if (enableAnimatedReply) { choiceToDeliver = choice.toString() // If the choice is animated reply, format the text by concatenating // attributionText with different color to choice text val fullTextWithAttribution = formatChoiceWithAttribution(choice) text = fullTextWithAttribution } else { choiceToDeliver = choice text = choice } val onClickListener = View.OnClickListener { onSmartReplyClick(entry, smartReplies, replyIndex, parent, this, choice) onSmartReplyClick( entry, smartReplies, replyIndex, parent, this, choiceToDeliver ) } setOnClickListener( if (delayOnClickListener) Loading Loading @@ -600,6 +629,47 @@ constructor( RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE) return intent } // Check if the choice is animated reply private fun isAnimatedReply(choice: CharSequence): Boolean { if (choice is Spanned) { val annotations = choice.getSpans(0, choice.length, Annotation::class.java) for (annotation in annotations) { if (annotation.key == "isAnimatedReply" && annotation.value == "1") { return true } } } return false } // Format the text by concatenating attributionText with attribution text color to choice text private fun formatChoiceWithAttribution(choice: CharSequence): CharSequence { val colorInt = context.getColor(R.color.animated_action_button_attribution_color) if (choice is Spanned) { val annotations = choice.getSpans(0, choice.length, Annotation::class.java) for (annotation in annotations) { if (annotation.key == "attributionText") { // Extract the attribution text val extraText = annotation.value // Concatenate choice text and attribution text val spannableWithColor = SpannableStringBuilder(choice) spannableWithColor.append(" $extraText") // Apply color to attribution text spannableWithColor.setSpan( ForegroundColorSpan(colorInt), choice.length, spannableWithColor.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) return spannableWithColor } } } // Return the original if no attributionText found return choice.toString() } } /** Loading Loading
packages/SystemUI/res/values/colors.xml +1 −0 Original line number Diff line number Diff line Loading @@ -148,6 +148,7 @@ <!-- Animated Action colors --> <color name="animated_action_button_text_color">@androidprv:color/materialColorOnSurface</color> <color name="animated_action_button_stroke_color">@androidprv:color/materialColorOnSurface</color> <color name="animated_action_button_attribution_color">@androidprv:color/materialColorOnSurfaceVariant</color> <!-- Biometric dialog colors --> <color name="biometric_dialog_gray">#ff757575</color> Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +73 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,10 @@ import android.graphics.drawable.Icon import android.os.Build import android.os.Bundle import android.os.SystemClock import android.text.Annotation import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.ForegroundColorSpan import android.util.Log import android.view.ContextThemeWrapper import android.view.LayoutInflater Loading Loading @@ -504,15 +508,40 @@ constructor( choice: CharSequence, delayOnClickListener: Boolean, ): Button { val layoutRes = val enableAnimatedReply = Flags.notificationAnimatedActionsTreatment() && smartReplies.fromAssistant && isAnimatedReply(choice) val layoutRes = if (enableAnimatedReply) { R.layout.animated_action_button } else { if (notificationsRedesignTemplates()) R.layout.notification_2025_smart_reply_button else R.layout.smart_reply_button } return (LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) as Button) .apply { // choiceToDeliver does not contain Annotation with extra data val choiceToDeliver: CharSequence if (enableAnimatedReply) { choiceToDeliver = choice.toString() // If the choice is animated reply, format the text by concatenating // attributionText with different color to choice text val fullTextWithAttribution = formatChoiceWithAttribution(choice) text = fullTextWithAttribution } else { choiceToDeliver = choice text = choice } val onClickListener = View.OnClickListener { onSmartReplyClick(entry, smartReplies, replyIndex, parent, this, choice) onSmartReplyClick( entry, smartReplies, replyIndex, parent, this, choiceToDeliver ) } setOnClickListener( if (delayOnClickListener) Loading Loading @@ -600,6 +629,47 @@ constructor( RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE) return intent } // Check if the choice is animated reply private fun isAnimatedReply(choice: CharSequence): Boolean { if (choice is Spanned) { val annotations = choice.getSpans(0, choice.length, Annotation::class.java) for (annotation in annotations) { if (annotation.key == "isAnimatedReply" && annotation.value == "1") { return true } } } return false } // Format the text by concatenating attributionText with attribution text color to choice text private fun formatChoiceWithAttribution(choice: CharSequence): CharSequence { val colorInt = context.getColor(R.color.animated_action_button_attribution_color) if (choice is Spanned) { val annotations = choice.getSpans(0, choice.length, Annotation::class.java) for (annotation in annotations) { if (annotation.key == "attributionText") { // Extract the attribution text val extraText = annotation.value // Concatenate choice text and attribution text val spannableWithColor = SpannableStringBuilder(choice) spannableWithColor.append(" $extraText") // Apply color to attribution text spannableWithColor.setSpan( ForegroundColorSpan(colorInt), choice.length, spannableWithColor.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) return spannableWithColor } } } // Return the original if no attributionText found return choice.toString() } } /** Loading