Add reply text section to notifications

Also fixes a potential NPE in the failed
inflation logging in SystemUI.

Bug: 22452379
Change-Id: I829c332f7f708de8374740f6d02a2bc10a33386b
This commit is contained in:
Adrian Roos
2015-12-08 16:17:19 -08:00
parent 1d724e02b0
commit e458aa8dbd
11 changed files with 187 additions and 2 deletions

View File

@@ -4851,6 +4851,7 @@ package android.app {
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";
@@ -5017,6 +5018,7 @@ package android.app {
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);

View File

@@ -4971,6 +4971,7 @@ package android.app {
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";
@@ -5137,6 +5138,7 @@ package android.app {
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);

View File

@@ -4851,6 +4851,7 @@ package android.app {
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";
@@ -5017,6 +5018,7 @@ package android.app {
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);

View File

@@ -144,6 +144,11 @@ public class Notification implements Parcelable
*/
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.
*
@@ -744,6 +749,12 @@ public class Notification implements Parcelable
*/
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)}.
@@ -2323,6 +2334,34 @@ public class Notification implements Parcelable
return this;
}
/**
* 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
@@ -3214,6 +3253,14 @@ public class Notification implements Parcelable
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) {
@@ -3221,18 +3268,62 @@ public class Notification implements Parcelable
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

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -62,6 +62,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" />

View File

@@ -432,6 +432,8 @@ please see styles_device_defaults.xml.
<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>

View File

@@ -2361,6 +2361,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="id" name="app_name_text" />
<java-symbol type="id" name="number_of_children" />

View File

@@ -2098,7 +2098,8 @@ public abstract class BaseStatusBar extends SystemUI implements
}
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) {