Add importance ring around conversation badge

Bug: 150905003
Test: manual
Change-Id: I967122fcf6404491fb514bc42e5f9e64490b8024
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1320d1d..b58a85e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7584,6 +7584,7 @@
                 isOneToOne = !isGroupConversation();
             }
             boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
+            boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT;
             Icon largeIcon = isConversationLayout ? mShortcutIcon : mBuilder.mN.mLargeIcon;
             TemplateBindResult bindResult = new TemplateBindResult();
             StandardTemplateParams p = mBuilder.mParams.reset()
@@ -7626,6 +7627,10 @@
                     isOneToOne);
             contentView.setCharSequence(R.id.status_bar_latest_event_content,
                     "setConversationTitle", conversationTitle);
+            if (isConversationLayout) {
+                contentView.setBoolean(R.id.status_bar_latest_event_content,
+                        "setIsImportantConversation", isImportantConversation);
+            }
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     largeIcon);
             contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 4028fda..f9aae0f 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -120,6 +120,7 @@
     private int mIconSizeBadged;
     private int mIconSizeCentered;
     private CachingIconView mIcon;
+    private View mImportanceRingView;
     private int mExpandedGroupTopMargin;
     private View mConversationFacePile;
     private int mNotificationBackgroundColor;
@@ -169,6 +170,7 @@
         mTextPaint.setAntiAlias(true);
         mConversationIcon = findViewById(R.id.conversation_icon);
         mIcon = findViewById(R.id.icon);
+        mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring);
         mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
         mIcon.setOnVisibilityChangedListener((visibility) -> {
             // Always keep the badge visibility in sync with the icon. This is necessary in cases
@@ -213,6 +215,14 @@
     }
 
     /**
+     * Sets this conversation as "important", adding some additional UI treatment.
+     */
+    @RemotableViewMethod
+    public void setIsImportantConversation(boolean isImportantConversation) {
+        mImportanceRingView.setVisibility(isImportantConversation ? VISIBLE : GONE);
+    }
+
+    /**
      * Set this layout to show the collapsed representation.
      *
      * @param isCollapsed is it collapsed
@@ -309,14 +319,12 @@
         updateTitleAndNamesDisplay();
 
         updateConversationLayout();
-
     }
 
     /**
      * Update the layout according to the data provided (i.e mIsOneToOne, expanded etc);
      */
     private void updateConversationLayout() {
-        // TODO: resolve this from shortcuts
         // Set avatar and name
         CharSequence conversationText = mConversationTitle;
         // TODO: display the secondary text somewhere
@@ -463,7 +471,7 @@
         int marginTop;
         int iconSize;
         if (mIsOneToOne || mIsCollapsed) {
-            // Baded format
+            // Badged format
             gravity = Gravity.LEFT;
             marginStart = mBadgedSideMargins;
             marginTop = mBadgedSideMargins;
@@ -479,11 +487,9 @@
         layoutParams.gravity = gravity;
         layoutParams.topMargin = marginTop;
         layoutParams.setMarginStart(marginStart);
+        layoutParams.width = iconSize;
+        layoutParams.height = iconSize;
         mConversationIconBadge.setLayoutParams(layoutParams);
-        ViewGroup.LayoutParams iconParams = mIcon.getLayoutParams();
-        iconParams.width = iconSize;
-        iconParams.height = iconSize;
-        mIcon.setLayoutParams(iconParams);
     }
 
     @RemotableViewMethod
diff --git a/core/res/res/drawable/conversation_badge_ring.xml b/core/res/res/drawable/conversation_badge_ring.xml
new file mode 100644
index 0000000..11ba8ad
--- /dev/null
+++ b/core/res/res/drawable/conversation_badge_ring.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+
+    <solid
+        android:color="@color/transparent"/>
+
+    <stroke
+        android:color="@color/conversation_important_highlight"
+        android:width="2dp"/>
+
+    <size
+        android:width="26dp"
+        android:height="26dp"/>
+</shape>
+
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 8246583..f79ea62 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -58,18 +58,32 @@
 
             <FrameLayout
                 android:id="@+id/conversation_icon_badge"
-                android:layout_width="20dp"
-                android:layout_height="20dp"
+                android:layout_width="@dimen/conversation_icon_size_badged"
+                android:layout_height="@dimen/conversation_icon_size_badged"
                 android:layout_marginLeft="@dimen/conversation_badge_side_margin"
                 android:layout_marginTop="@dimen/conversation_badge_side_margin"
-                android:background="@drawable/conversation_badge_background" >
+            >
+                <ImageView
+                    android:id="@+id/conversation_icon_badge_bg"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:src="@drawable/conversation_badge_background"
+                />
                 <!-- Badge: 20x20, 48dp padding left + top -->
                 <com.android.internal.widget.CachingIconView
                     android:id="@+id/icon"
-                    android:layout_width="@dimen/conversation_icon_size_badged"
-                    android:layout_height="@dimen/conversation_icon_size_badged"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:padding="4dp"
                     android:layout_gravity="center"
                 />
+                <ImageView
+                    android:id="@+id/conversation_icon_badge_ring"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:src="@drawable/conversation_badge_ring"
+                    android:visibility="gone"
+                />
             </FrameLayout>
         </FrameLayout>
     </FrameLayout>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 91248f1..831da6f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -226,4 +226,6 @@
     <color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
     <color name="resolver_empty_state_text">#FF202124</color>
     <color name="resolver_empty_state_icon">#FF5F6368</color>
+
+    <color name="conversation_important_highlight">#F9AB00</color>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6fdc223..abc9692 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -692,10 +692,10 @@
     <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen>
     <!-- Side margins of the conversation badge in relation to the conversation icon -->
     <dimen name="conversation_badge_side_margin">36dp</dimen>
-    <!-- size of the notification icon when badged in a conversation -->
-    <dimen name="conversation_icon_size_badged">15dp</dimen>
-    <!-- size of the notification icon when centered in a conversation -->
-    <dimen name="conversation_icon_size_centered">20dp</dimen>
+    <!-- size of the notification badge when applied to the conversation icon -->
+    <dimen name="conversation_icon_size_badged">20dp</dimen>
+    <!-- size of the notification badge when centered in a conversation -->
+    <dimen name="conversation_icon_size_centered">25dp</dimen>
     <!-- margin on the top when the icon is centered for group conversations -->
     <dimen name="conversation_icon_margin_top_centered">5dp</dimen>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 103dba4..95fb6857 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3860,6 +3860,8 @@
   <java-symbol type="string" name="conversation_title_fallback_group_chat" />
   <java-symbol type="id" name="conversation_icon" />
   <java-symbol type="id" name="conversation_icon_badge" />
+  <java-symbol type="id" name="conversation_icon_badge_ring" />
+  <java-symbol type="id" name="conversation_icon_badge_bg" />
   <java-symbol type="id" name="expand_button_container" />
   <java-symbol type="id" name="messaging_group_content_container" />
   <java-symbol type="id" name="expand_button_and_content_container" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 2a45bc2..83e51cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -49,6 +49,14 @@
         mTransformedViews.put(key, transformedView);
     }
 
+    public void addTransformedView(View transformedView) {
+        int key = transformedView.getId();
+        if (key == View.NO_ID) {
+            throw new IllegalArgumentException("View argument does not have a valid id");
+        }
+        addTransformedView(key, transformedView);
+    }
+
     /**
      * Add a view that transforms to a similar sibling, meaning that we should consider any mapping
      * found treated as the same viewType. This is useful for imageViews, where it's hard to compare
@@ -62,6 +70,14 @@
         mKeysTransformingToSimilar.add(key);
     }
 
+    public void addViewTransformingToSimilar(View transformedView) {
+        int key = transformedView.getId();
+        if (key == View.NO_ID) {
+            throw new IllegalArgumentException("View argument does not have a valid id");
+        }
+        addViewTransformingToSimilar(key, transformedView);
+    }
+
     public void reset() {
         mTransformedViews.clear();
         mKeysTransformingToSimilar.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 8b6081e..53b00f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -30,41 +30,50 @@
 import com.android.systemui.statusbar.notification.row.HybridNotificationView
 
 /**
- * Wraps a notification containing a converation template
+ * Wraps a notification containing a conversation template
  */
 class NotificationConversationTemplateViewWrapper constructor(
     ctx: Context,
     view: View,
     row: ExpandableNotificationRow
-)
-    : NotificationTemplateViewWrapper(ctx, view, row) {
+) : NotificationTemplateViewWrapper(ctx, view, row) {
 
-    private val minHeightWithActions: Int
-    private val conversationLayout: ConversationLayout
-    private var conversationIcon: View? = null
-    private var conversationBadge: View? = null
-    private var expandButton: View? = null
+    private val minHeightWithActions: Int = NotificationUtils.getFontScaledHeight(
+            ctx,
+            R.dimen.notification_messaging_actions_min_height
+    )
+    private val conversationLayout: ConversationLayout = view as ConversationLayout
+
+    private lateinit var conversationIcon: View
+    private lateinit var conversationBadge: View
+    private lateinit var conversationBadgeBg: View
+    private lateinit var expandButton: View
     private lateinit var expandButtonContainer: View
     private lateinit var imageMessageContainer: ViewGroup
-    private var messagingLinearLayout: MessagingLinearLayout? = null
-
-    init {
-        conversationLayout = view as ConversationLayout
-        minHeightWithActions = NotificationUtils.getFontScaledHeight(ctx,
-                R.dimen.notification_messaging_actions_min_height)
-    }
+    private lateinit var messagingLinearLayout: MessagingLinearLayout
+    private lateinit var importanceRing: View
 
     private fun resolveViews() {
         messagingLinearLayout = conversationLayout.messagingLinearLayout
         imageMessageContainer = conversationLayout.imageMessageContainer
         conversationIcon = conversationLayout.requireViewById(
-                com.android.internal.R.id.conversation_icon)
+                com.android.internal.R.id.conversation_icon
+        )
         conversationBadge = conversationLayout.requireViewById(
-                com.android.internal.R.id.conversation_icon_badge)
+                com.android.internal.R.id.conversation_icon_badge
+        )
+        conversationBadgeBg = conversationLayout.requireViewById(
+                com.android.internal.R.id.conversation_icon_badge_bg
+        )
         expandButton = conversationLayout.requireViewById(
-                com.android.internal.R.id.expand_button)
+                com.android.internal.R.id.expand_button
+        )
         expandButtonContainer = conversationLayout.requireViewById(
-                com.android.internal.R.id.expand_button_container)
+                com.android.internal.R.id.expand_button_container
+        )
+        importanceRing = conversationLayout.requireViewById(
+                com.android.internal.R.id.conversation_icon_badge_ring
+        )
     }
 
     override fun onContentUpdated(row: ExpandableNotificationRow) {
@@ -77,71 +86,67 @@
     override fun updateTransformedTypes() {
         // This also clears the existing types
         super.updateTransformedTypes()
-        messagingLinearLayout?.let {
-            mTransformationHelper.addTransformedView(it.id, it)
-        }
+
+        addTransformedViews(messagingLinearLayout)
 
         // Let's ignore the image message container since that is transforming as part of the
         // messages already
         mTransformationHelper.setCustomTransformation(
                 object : ViewTransformationHelper.CustomTransformation() {
-            override fun transformTo(ownState: TransformState,
-                                     otherView: TransformableView,
-                                     transformationAmount: Float): Boolean {
-                if (otherView is HybridNotificationView) {
-                    return false
-                }
-                // we're hidden by default by the transformState
-                ownState.ensureVisible();
-                // Let's do nothing otherwise, this is already handled by the messages
-                return true
-            }
+                    override fun transformTo(
+                        ownState: TransformState,
+                        otherView: TransformableView,
+                        transformationAmount: Float
+                    ): Boolean {
+                        if (otherView is HybridNotificationView) {
+                            return false
+                        }
+                        // we're hidden by default by the transformState
+                        ownState.ensureVisible()
+                        // Let's do nothing otherwise, this is already handled by the messages
+                        return true
+                    }
 
-            override fun transformFrom(ownState: TransformState,
-                                       otherView: TransformableView,
-                                       transformationAmount: Float): Boolean {
-                if (otherView is HybridNotificationView) {
-                    return false
-                }
-                // we're hidden by default by the transformState
-                ownState.ensureVisible();
-                // Let's do nothing otherwise, this is already handled by the messages
-                return true
-            }
-        }, imageMessageContainer.id)
+                    override fun transformFrom(
+                        ownState: TransformState,
+                        otherView: TransformableView,
+                        transformationAmount: Float
+                    ): Boolean =
+                            transformTo(ownState, otherView, transformationAmount)
+                },
+                imageMessageContainer.id
+        )
 
-        conversationIcon?.let {
-            mTransformationHelper.addViewTransformingToSimilar(it.id, it)
-        }
-        conversationBadge?.let {
-            mTransformationHelper.addViewTransformingToSimilar(it.id, it)
-        }
-        expandButton?.let {
-            mTransformationHelper.addViewTransformingToSimilar(it.id, it)
-        }
+        addViewsTransformingToSimilar(
+                conversationIcon,
+                conversationBadge,
+                conversationBadgeBg,
+                expandButton,
+                importanceRing
+        )
     }
 
-    override fun setRemoteInputVisible(visible: Boolean) {
-        conversationLayout.showHistoricMessages(visible)
-    }
+    override fun setRemoteInputVisible(visible: Boolean) =
+            conversationLayout.showHistoricMessages(visible)
 
-    override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) {
-        conversationLayout.updateExpandability(expandable, onClickListener)
-    }
+    override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) =
+            conversationLayout.updateExpandability(expandable, onClickListener)
 
     override fun disallowSingleClick(x: Float, y: Float): Boolean {
-        if (expandButtonContainer.visibility == View.VISIBLE
-                && isOnView(expandButtonContainer, x, y)) {
-            return true
-        }
-        return super.disallowSingleClick(x, y)
+        val isOnExpandButton = expandButtonContainer.visibility == View.VISIBLE &&
+                isOnView(expandButtonContainer, x, y)
+        return isOnExpandButton || super.disallowSingleClick(x, y)
     }
 
-    override fun getMinLayoutHeight(): Int {
-        if (mActionsContainer != null && mActionsContainer.visibility != View.GONE) {
-            return minHeightWithActions
-        } else {
-            return super.getMinLayoutHeight()
-        }
-    }
+    override fun getMinLayoutHeight(): Int =
+            if (mActionsContainer != null && mActionsContainer.visibility != View.GONE)
+                minHeightWithActions
+            else
+                super.getMinLayoutHeight()
+
+    private fun addTransformedViews(vararg vs: View) =
+            vs.forEach(mTransformationHelper::addTransformedView)
+
+    private fun addViewsTransformingToSimilar(vararg vs: View) =
+            vs.forEach(mTransformationHelper::addViewTransformingToSimilar)
 }