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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
61
core/res/res/layout/notification_material_reply_text.xml
Normal file
61
core/res/res/layout/notification_material_reply_text.xml
Normal 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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user