Merge "Add reply text section to notifications"
diff --git a/api/current.txt b/api/current.txt
index ec65b25..3854b7a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4838,6 +4838,7 @@
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
     field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
     field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
     field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -5004,6 +5005,7 @@
     method public android.app.Notification.Builder setPriority(int);
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
+    method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
     method public android.app.Notification.Builder setShowWhen(boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
     method public android.app.Notification.Builder setSmallIcon(int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index e992a77..c2fc9a6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4958,6 +4958,7 @@
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
     field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
     field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
     field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -5124,6 +5125,7 @@
     method public android.app.Notification.Builder setPriority(int);
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
+    method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
     method public android.app.Notification.Builder setShowWhen(boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
     method public android.app.Notification.Builder setSmallIcon(int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 42a6a86..83d12fc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4838,6 +4838,7 @@
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
     field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
     field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
     field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -5004,6 +5005,7 @@
     method public android.app.Notification.Builder setPriority(int);
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
+    method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
     method public android.app.Notification.Builder setShowWhen(boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
     method public android.app.Notification.Builder setSmallIcon(int, int);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3ff0896..a392abd 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -145,6 +145,11 @@
     private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
 
     /**
+     * Maximum entries of reply text that are accepted by Builder and friends.
+     */
+    private static final int MAX_REPLY_HISTORY = 5;
+
+    /**
      * A timestamp related to this notification, in milliseconds since the epoch.
      *
      * Default value: {@link System#currentTimeMillis() Now}.
@@ -745,6 +750,12 @@
     public static final String EXTRA_SUB_TEXT = "android.subText";
 
     /**
+     * {@link #extras} key: this is the remote input history, as supplied to
+     * {@link Builder#setRemoteInputHistory(CharSequence[])}.
+     */
+    public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+
+    /**
      * {@link #extras} key: this is a small piece of additional text as supplied to
      * {@link Builder#setContentInfo(CharSequence)}.
      */
@@ -2324,6 +2335,34 @@
         }
 
         /**
+         * Set the remote input history.
+         *
+         * This should be set to the most recent inputs that have been sent
+         * through a {@link RemoteInput} of this Notification and cleared once the it is no
+         * longer relevant (e.g. for chat notifications once the other party has responded).
+         *
+         * The most recent input must be stored at the 0 index, the second most recent at the
+         * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
+         * and how much of each individual input is shown.
+         *
+         * <p>Note: The reply text will only be shown on notifications that have least one action
+         * with a {@code RemoteInput}.</p>
+         */
+        public Builder setRemoteInputHistory(CharSequence[] text) {
+            if (text == null) {
+                mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
+            } else {
+                final int N = Math.min(MAX_REPLY_HISTORY, text.length);
+                CharSequence[] safe = new CharSequence[N];
+                for (int i = 0; i < N; i++) {
+                    safe[i] = safeCharSequence(text[i]);
+                }
+                mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
+            }
+            return this;
+        }
+
+        /**
          * Set the large number at the right-hand side of the notification.  This is
          * equivalent to setContentInfo, although it might show the number in a different
          * font size for readability.
@@ -3232,6 +3271,14 @@
         private void resetStandardTemplateWithActions(RemoteViews big) {
             big.setViewVisibility(R.id.actions, View.GONE);
             big.removeAllViews(R.id.actions);
+
+            big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
+            big.setTextViewText(R.id.notification_material_reply_text_1, null);
+
+            big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
+            big.setTextViewText(R.id.notification_material_reply_text_2, null);
+            big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
+            big.setTextViewText(R.id.notification_material_reply_text_3, null);
         }
 
         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
@@ -3239,18 +3286,62 @@
 
             resetStandardTemplateWithActions(big);
 
+            boolean validRemoteInput = false;
+
             int N = mActions.size();
             if (N > 0) {
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 for (int i=0; i<N; i++) {
-                    final RemoteViews button = generateActionButton(mActions.get(i));
+                    Action action = mActions.get(i);
+                    validRemoteInput |= hasValidRemoteInput(action);
+
+                    final RemoteViews button = generateActionButton(action);
                     big.addView(R.id.actions, button);
                 }
             }
+
+            CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
+            if (validRemoteInput && replyText != null
+                    && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
+                big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
+                big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
+
+                if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
+                    big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
+                    big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
+
+                    if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
+                        big.setViewVisibility(
+                                R.id.notification_material_reply_text_3, View.VISIBLE);
+                        big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
+                    }
+                }
+            }
+
             return big;
         }
 
+        private boolean hasValidRemoteInput(Action action) {
+            if (TextUtils.isEmpty(action.title) || action.actionIntent == null) {
+                // Weird actions
+                return false;
+            }
+
+            RemoteInput[] remoteInputs = action.getRemoteInputs();
+            if (remoteInputs == null) {
+                return false;
+            }
+
+            for (RemoteInput r : remoteInputs) {
+                CharSequence[] choices = r.getChoices();
+                if (r.getAllowFreeFormInput() || (choices != null && choices.length != 0)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         /**
          * Construct a RemoteViews for the final 1U notification layout. In order:
          *   1. Custom contentView from the caller
diff --git a/core/res/res/layout/notification_material_reply_text.xml b/core/res/res/layout/notification_material_reply_text.xml
new file mode 100644
index 0000000..bc22eb4
--- /dev/null
+++ b/core/res/res/layout/notification_material_reply_text.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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
+  -->
+
+<!-- Note: this layout is included from a view stub; layout attributes will be overridden. -->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/notification_material_reply_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingStart="@dimen/notification_content_margin_start">
+
+    <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:id="@+id/action_divider"
+            android:layout_marginBottom="15dp"
+            android:background="@drawable/notification_template_divider" />
+
+    <TextView
+            android:id="@+id/notification_material_reply_text_3"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:visibility="gone"
+            android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+            android:singleLine="true" />
+
+    <TextView
+            android:id="@+id/notification_material_reply_text_2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:visibility="gone"
+            android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+            android:singleLine="true" />
+
+    <TextView
+            android:id="@+id/notification_material_reply_text_1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dp"
+            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+            android:singleLine="true" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 91a5ceb..dfd72c0 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -55,6 +55,11 @@
         </FrameLayout>
         <include layout="@layout/notification_template_right_icon" />
     </FrameLayout>
+    <ViewStub android:layout="@layout/notification_material_reply_text"
+            android:id="@+id/notification_material_reply_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+    />
     <include
         layout="@layout/notification_material_action_list"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 58e3d1b..5c07b9b 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -52,6 +52,13 @@
                 android:layout_marginBottom="16dp"
                 android:scaleType="centerCrop"
                 />
+        <ViewStub android:layout="@layout/notification_material_reply_text"
+                android:id="@+id/notification_material_reply_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="-16dp"
+                android:layout_marginEnd="-16dp"
+                />
         <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index de9f572..ebaffc3 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -63,6 +63,13 @@
                 android:contentDescription="@string/notification_work_profile_content_description"
                 />
         </LinearLayout>
+        <ViewStub android:layout="@layout/notification_material_reply_text"
+                android:id="@+id/notification_material_reply_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="-16dp"
+                android:layout_marginEnd="-16dp"
+        />
         <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
     <include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 74ebf26..8485e59 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -432,6 +432,8 @@
         <item name="textSize">@dimen/notification_text_size</item>
     </style>
 
+    <style name="TextAppearance.Material.Notification.Reply" />
+
     <style name="TextAppearance.Material.Notification.Title">
         <item name="textColor">@color/primary_text_default_material_light</item>
         <item name="textSize">@dimen/notification_title_text_size</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b40fc3b..3c1d944 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2366,6 +2366,11 @@
   <java-symbol type="id" name="addToDictionaryButton" />
   <java-symbol type="id" name="deleteButton" />
 
+  <java-symbol type="id" name="notification_material_reply_container" />
+  <java-symbol type="id" name="notification_material_reply_text_1" />
+  <java-symbol type="id" name="notification_material_reply_text_2" />
+  <java-symbol type="id" name="notification_material_reply_text_3" />
+
   <java-symbol type="string" name="notification_children_count_bracketed" />
   <java-symbol type="string" name="notification_hidden_text" />
   <java-symbol type="id" name="app_name_text" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 3537d3e..1d7651c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1990,7 +1990,8 @@
             }
             catch (RuntimeException e) {
                 // It failed to apply cleanly.
-                Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
+                Log.w(TAG, "Couldn't reapply views for package " +
+                        notification.getPackageName(), e);
             }
         }
         if (!updateSuccessful) {