Add RemoteInput, Grouping, and Extender to Notification api.
RemoteInputs annotate actions and content intents to request
input from the user as part of an intent being sent. Results
are sent along using ClipData to avoid unparcel of developer
provided bundle values. A helper method is expexted to be used
to extract results.
Grouping allows notifications to be bundled together, with an optional
summary notification for display on older platforms. SortKey is an
important part of grouping since child notifications will likely have
a prescribed ordering. It is also useful in top level notifications
for apps that want to provide an ordering all other fields being equal.
Also provide a fluid way to to extend Actions and Notifications
using Extenders, e.g.:
Notification n = new NotificationCompat.Builder(context)
.setOption1()
.apply(new SomeExtender()
.setOption2())
.build();
This helps extension libraries provide a nice API experience for devs.
Change-Id: Ib3438ef854772c2c34d21bf1eb4ed7c9e032106f
This commit is contained in:
@@ -3916,6 +3916,8 @@ package android.app {
|
||||
ctor public Notification(android.os.Parcel);
|
||||
method public android.app.Notification clone();
|
||||
method public int describeContents();
|
||||
method public java.lang.String getGroup();
|
||||
method public java.lang.String getSortKey();
|
||||
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
@@ -3942,6 +3944,7 @@ package android.app {
|
||||
field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
|
||||
field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
|
||||
field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
|
||||
field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
|
||||
field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
|
||||
field public static final int FLAG_INSISTENT = 4; // 0x4
|
||||
field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
|
||||
@@ -3985,6 +3988,7 @@ package android.app {
|
||||
method public android.app.Notification.Action clone();
|
||||
method public int describeContents();
|
||||
method public android.os.Bundle getExtras();
|
||||
method public android.app.RemoteInput[] getRemoteInputs();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
field public android.app.PendingIntent actionIntent;
|
||||
@@ -3992,14 +3996,20 @@ package android.app {
|
||||
field public java.lang.CharSequence title;
|
||||
}
|
||||
|
||||
public static class Notification.Action.Builder {
|
||||
public static final class Notification.Action.Builder {
|
||||
ctor public Notification.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
|
||||
ctor public Notification.Action.Builder(android.app.Notification.Action);
|
||||
method public android.app.Notification.Action.Builder addExtras(android.os.Bundle);
|
||||
method public android.app.Notification.Action.Builder addRemoteInput(android.app.RemoteInput);
|
||||
method public android.app.Notification.Action.Builder apply(android.app.Notification.Action.Builder.Extender);
|
||||
method public android.app.Notification.Action build();
|
||||
method public android.os.Bundle getExtras();
|
||||
}
|
||||
|
||||
public static abstract interface Notification.Action.Builder.Extender {
|
||||
method public abstract android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
|
||||
}
|
||||
|
||||
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
|
||||
ctor public Notification.BigPictureStyle();
|
||||
ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
|
||||
@@ -4022,6 +4032,7 @@ package android.app {
|
||||
method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
|
||||
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
|
||||
method public android.app.Notification.Builder addExtras(android.os.Bundle);
|
||||
method public android.app.Notification.Builder apply(android.app.Notification.Builder.Extender);
|
||||
method public android.app.Notification build();
|
||||
method public android.os.Bundle getExtras();
|
||||
method public deprecated android.app.Notification getNotification();
|
||||
@@ -4035,6 +4046,8 @@ package android.app {
|
||||
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
|
||||
method public android.app.Notification.Builder setExtras(android.os.Bundle);
|
||||
method public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
|
||||
method public android.app.Notification.Builder setGroup(java.lang.String);
|
||||
method public android.app.Notification.Builder setGroupSummary(boolean);
|
||||
method public android.app.Notification.Builder setLargeIcon(android.graphics.Bitmap);
|
||||
method public android.app.Notification.Builder setLights(int, int, int);
|
||||
method public android.app.Notification.Builder setLocalOnly(boolean);
|
||||
@@ -4046,6 +4059,7 @@ package android.app {
|
||||
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);
|
||||
method public android.app.Notification.Builder setSortKey(java.lang.String);
|
||||
method public android.app.Notification.Builder setSound(android.net.Uri);
|
||||
method public android.app.Notification.Builder setSound(android.net.Uri, int);
|
||||
method public android.app.Notification.Builder setStyle(android.app.Notification.Style);
|
||||
@@ -4057,6 +4071,10 @@ package android.app {
|
||||
method public android.app.Notification.Builder setWhen(long);
|
||||
}
|
||||
|
||||
public static abstract interface Notification.Builder.Extender {
|
||||
method public abstract android.app.Notification.Builder applyTo(android.app.Notification.Builder);
|
||||
}
|
||||
|
||||
public static class Notification.InboxStyle extends android.app.Notification.Style {
|
||||
ctor public Notification.InboxStyle();
|
||||
ctor public Notification.InboxStyle(android.app.Notification.Builder);
|
||||
@@ -4160,6 +4178,31 @@ package android.app {
|
||||
field public static final int STYLE_SPINNER = 0; // 0x0
|
||||
}
|
||||
|
||||
public final class RemoteInput implements android.os.Parcelable {
|
||||
method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
|
||||
method public int describeContents();
|
||||
method public boolean getAllowFreeFormInput();
|
||||
method public java.lang.CharSequence[] getChoices();
|
||||
method public android.os.Bundle getExtras();
|
||||
method public java.lang.CharSequence getLabel();
|
||||
method public java.lang.String getResultKey();
|
||||
method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
|
||||
field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
|
||||
}
|
||||
|
||||
public static final class RemoteInput.Builder {
|
||||
ctor public RemoteInput.Builder(java.lang.String);
|
||||
method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
|
||||
method public android.app.RemoteInput build();
|
||||
method public android.os.Bundle getExtras();
|
||||
method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
|
||||
method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
|
||||
method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
|
||||
}
|
||||
|
||||
public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
|
||||
method public android.content.ComponentName getGlobalSearchActivity();
|
||||
method public android.app.SearchableInfo getSearchableInfo(android.content.ComponentName);
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.android.internal.R;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* A class that represents how a persistent notification is to be presented to
|
||||
@@ -355,6 +356,14 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
public static final int FLAG_LOCAL_ONLY = 0x00000100;
|
||||
|
||||
/**
|
||||
* Bit to be bitswise-ored into the {@link #flags} field that should be
|
||||
* set if this notification is the group summary for a group of notifications.
|
||||
* Grouped notifications may display in a cluster or stack on devices which
|
||||
* support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
|
||||
*/
|
||||
public static final int FLAG_GROUP_SUMMARY = 0x00000200;
|
||||
|
||||
public int flags;
|
||||
|
||||
/**
|
||||
@@ -493,6 +502,34 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
public String category;
|
||||
|
||||
private String mGroupKey;
|
||||
|
||||
/**
|
||||
* Get the key used to group this notification into a cluster or stack
|
||||
* with other notifications on devices which support such rendering.
|
||||
*/
|
||||
public String getGroup() {
|
||||
return mGroupKey;
|
||||
}
|
||||
|
||||
private String mSortKey;
|
||||
|
||||
/**
|
||||
* Get a sort key that orders this notification among other notifications from the
|
||||
* same package. This can be useful if an external sort was already applied and an app
|
||||
* would like to preserve this. Notifications will be sorted lexicographically using this
|
||||
* value, although providing different priorities in addition to providing sort key may
|
||||
* cause this value to be ignored.
|
||||
*
|
||||
* <p>This sort key can also be used to order members of a notification group. See
|
||||
* {@link Builder#setGroup}.
|
||||
*
|
||||
* @see String#compareTo(String)
|
||||
*/
|
||||
public String getSortKey() {
|
||||
return mSortKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional semantic data to be carried around with this Notification.
|
||||
* <p>
|
||||
@@ -659,15 +696,18 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
public static class Action implements Parcelable {
|
||||
private final Bundle mExtras;
|
||||
private RemoteInput[] mRemoteInputs;
|
||||
|
||||
/**
|
||||
* Small icon representing the action.
|
||||
*/
|
||||
public int icon;
|
||||
|
||||
/**
|
||||
* Title of the action.
|
||||
*/
|
||||
public CharSequence title;
|
||||
|
||||
/**
|
||||
* Intent to send when the user invokes this action. May be null, in which case the action
|
||||
* may be rendered in a disabled presentation by the system UI.
|
||||
@@ -681,19 +721,23 @@ public class Notification implements Parcelable
|
||||
actionIntent = PendingIntent.CREATOR.createFromParcel(in);
|
||||
}
|
||||
mExtras = in.readBundle();
|
||||
mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}.
|
||||
*/
|
||||
public Action(int icon, CharSequence title, PendingIntent intent) {
|
||||
this(icon, title, intent, new Bundle());
|
||||
this(icon, title, intent, new Bundle(), null);
|
||||
}
|
||||
|
||||
private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
|
||||
private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
|
||||
RemoteInput[] remoteInputs) {
|
||||
this.icon = icon;
|
||||
this.title = title;
|
||||
this.actionIntent = intent;
|
||||
this.mExtras = extras != null ? extras : new Bundle();
|
||||
this.mRemoteInputs = remoteInputs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,14 +747,23 @@ public class Notification implements Parcelable
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of inputs to be collected from the user when this action is sent.
|
||||
* May return null if no remote inputs were added.
|
||||
*/
|
||||
public RemoteInput[] getRemoteInputs() {
|
||||
return mRemoteInputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link Action} objects.
|
||||
*/
|
||||
public static class Builder {
|
||||
public static final class Builder {
|
||||
private final int mIcon;
|
||||
private final CharSequence mTitle;
|
||||
private final PendingIntent mIntent;
|
||||
private final Bundle mExtras;
|
||||
private ArrayList<RemoteInput> mRemoteInputs;
|
||||
|
||||
/**
|
||||
* Construct a new builder for {@link Action} object.
|
||||
@@ -719,7 +772,7 @@ public class Notification implements Parcelable
|
||||
* @param intent the {@link PendingIntent} to fire when users trigger this action
|
||||
*/
|
||||
public Builder(int icon, CharSequence title, PendingIntent intent) {
|
||||
this(icon, title, intent, new Bundle());
|
||||
this(icon, title, intent, new Bundle(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -728,14 +781,20 @@ public class Notification implements Parcelable
|
||||
* @param action the action to read fields from.
|
||||
*/
|
||||
public Builder(Action action) {
|
||||
this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
|
||||
this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
|
||||
action.getRemoteInputs());
|
||||
}
|
||||
|
||||
private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
|
||||
private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
|
||||
RemoteInput[] remoteInputs) {
|
||||
mIcon = icon;
|
||||
mTitle = title;
|
||||
mIntent = intent;
|
||||
mExtras = extras;
|
||||
if (remoteInputs != null) {
|
||||
mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
|
||||
Collections.addAll(mRemoteInputs, remoteInputs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -761,23 +820,63 @@ public class Notification implements Parcelable
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an input to be collected from the user when this action is sent.
|
||||
* Response values can be retrieved from the fired intent by using the
|
||||
* {@link RemoteInput#getResultsFromIntent} function.
|
||||
* @param remoteInput a {@link RemoteInput} to add to the action
|
||||
* @return this object for method chaining
|
||||
*/
|
||||
public Builder addRemoteInput(RemoteInput remoteInput) {
|
||||
if (mRemoteInputs == null) {
|
||||
mRemoteInputs = new ArrayList<RemoteInput>();
|
||||
}
|
||||
mRemoteInputs.add(remoteInput);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an extender to this action builder. Extenders may be used to add
|
||||
* metadata or change options on this builder.
|
||||
*/
|
||||
public Builder apply(Extender extender) {
|
||||
extender.applyTo(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extender interface for use with {@link #apply}. Extenders may be used to add
|
||||
* metadata or change options on this builder.
|
||||
*/
|
||||
public interface Extender {
|
||||
/**
|
||||
* Apply this extender to a notification action builder.
|
||||
* @param builder the builder to be modified.
|
||||
* @return the build object for chaining.
|
||||
*/
|
||||
public Builder applyTo(Builder builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all of the options that have been set and return a new {@link Action}
|
||||
* object.
|
||||
* @return the built action
|
||||
*/
|
||||
public Action build() {
|
||||
return new Action(mIcon, mTitle, mIntent, mExtras);
|
||||
RemoteInput[] remoteInputs = mRemoteInputs != null
|
||||
? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
|
||||
return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action clone() {
|
||||
return new Action(
|
||||
this.icon,
|
||||
this.title,
|
||||
this.actionIntent, // safe to alias
|
||||
new Bundle(this.mExtras));
|
||||
icon,
|
||||
title,
|
||||
actionIntent, // safe to alias
|
||||
new Bundle(mExtras),
|
||||
getRemoteInputs());
|
||||
}
|
||||
@Override
|
||||
public int describeContents() {
|
||||
@@ -794,6 +893,7 @@ public class Notification implements Parcelable
|
||||
out.writeInt(0);
|
||||
}
|
||||
out.writeBundle(mExtras);
|
||||
out.writeTypedArray(mRemoteInputs, flags);
|
||||
}
|
||||
public static final Parcelable.Creator<Action> CREATOR =
|
||||
new Parcelable.Creator<Action>() {
|
||||
@@ -906,6 +1006,10 @@ public class Notification implements Parcelable
|
||||
|
||||
category = parcel.readString();
|
||||
|
||||
mGroupKey = parcel.readString();
|
||||
|
||||
mSortKey = parcel.readString();
|
||||
|
||||
extras = parcel.readBundle(); // may be null
|
||||
|
||||
actions = parcel.createTypedArray(Action.CREATOR); // may be null
|
||||
@@ -971,6 +1075,10 @@ public class Notification implements Parcelable
|
||||
|
||||
that.category = this.category;
|
||||
|
||||
that.mGroupKey = this.mGroupKey;
|
||||
|
||||
that.mSortKey = this.mSortKey;
|
||||
|
||||
if (this.extras != null) {
|
||||
try {
|
||||
that.extras = new Bundle(this.extras);
|
||||
@@ -1108,6 +1216,10 @@ public class Notification implements Parcelable
|
||||
|
||||
parcel.writeString(category);
|
||||
|
||||
parcel.writeString(mGroupKey);
|
||||
|
||||
parcel.writeString(mSortKey);
|
||||
|
||||
parcel.writeBundle(extras); // null ok
|
||||
|
||||
parcel.writeTypedArray(actions, 0); // null ok
|
||||
@@ -1226,7 +1338,18 @@ public class Notification implements Parcelable
|
||||
sb.append(Integer.toHexString(this.defaults));
|
||||
sb.append(" flags=0x");
|
||||
sb.append(Integer.toHexString(this.flags));
|
||||
sb.append(" category="); sb.append(this.category);
|
||||
if (this.category != null) {
|
||||
sb.append(" category=");
|
||||
sb.append(this.category);
|
||||
}
|
||||
if (this.mGroupKey != null) {
|
||||
sb.append(" groupKey=");
|
||||
sb.append(this.mGroupKey);
|
||||
}
|
||||
if (this.mSortKey != null) {
|
||||
sb.append(" sortKey=");
|
||||
sb.append(this.mSortKey);
|
||||
}
|
||||
if (actions != null) {
|
||||
sb.append(" ");
|
||||
sb.append(actions.length);
|
||||
@@ -1306,6 +1429,8 @@ public class Notification implements Parcelable
|
||||
private int mProgress;
|
||||
private boolean mProgressIndeterminate;
|
||||
private String mCategory;
|
||||
private String mGroupKey;
|
||||
private String mSortKey;
|
||||
private Bundle mExtras;
|
||||
private int mPriority;
|
||||
private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
|
||||
@@ -1717,6 +1842,51 @@ public class Notification implements Parcelable
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this notification to be part of a group of notifications sharing the same key.
|
||||
* Grouped notifications may display in a cluster or stack on devices which
|
||||
* support such rendering.
|
||||
*
|
||||
* <p>To make this notification the summary for its group, also call
|
||||
* {@link #setGroupSummary}. A sort order can be specified for group members by using
|
||||
* {@link #setSortKey}.
|
||||
* @param groupKey The group key of the group.
|
||||
* @return this object for method chaining
|
||||
*/
|
||||
public Builder setGroup(String groupKey) {
|
||||
mGroupKey = groupKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this notification to be the group summary for a group of notifications.
|
||||
* Grouped notifications may display in a cluster or stack on devices which
|
||||
* support such rendering. Requires a group key also be set using {@link #setGroup}.
|
||||
* @param isGroupSummary Whether this notification should be a group summary.
|
||||
* @return this object for method chaining
|
||||
*/
|
||||
public Builder setGroupSummary(boolean isGroupSummary) {
|
||||
setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a sort key that orders this notification among other notifications from the
|
||||
* same package. This can be useful if an external sort was already applied and an app
|
||||
* would like to preserve this. Notifications will be sorted lexicographically using this
|
||||
* value, although providing different priorities in addition to providing sort key may
|
||||
* cause this value to be ignored.
|
||||
*
|
||||
* <p>This sort key can also be used to order members of a notification group. See
|
||||
* {@link Builder#setGroup}.
|
||||
*
|
||||
* @see String#compareTo(String)
|
||||
*/
|
||||
public Builder setSortKey(String sortKey) {
|
||||
mSortKey = sortKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge additional metadata into this notification.
|
||||
*
|
||||
@@ -1826,6 +1996,28 @@ public class Notification implements Parcelable
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an extender to this notification builder. Extenders may be used to add
|
||||
* metadata or change options on this builder.
|
||||
*/
|
||||
public Builder apply(Extender extender) {
|
||||
extender.applyTo(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extender interface for use with {@link #apply}. Extenders may be used to add
|
||||
* metadata or change options on this builder.
|
||||
*/
|
||||
public interface Extender {
|
||||
/**
|
||||
* Apply this extender to a notification builder.
|
||||
* @param builder the builder to be modified.
|
||||
* @return the build object for chaining.
|
||||
*/
|
||||
public Builder applyTo(Builder builder);
|
||||
}
|
||||
|
||||
private void setFlag(int mask, boolean value) {
|
||||
if (value) {
|
||||
mFlags |= mask;
|
||||
@@ -2028,12 +2220,13 @@ public class Notification implements Parcelable
|
||||
n.flags |= FLAG_SHOW_LIGHTS;
|
||||
}
|
||||
n.category = mCategory;
|
||||
n.mGroupKey = mGroupKey;
|
||||
n.mSortKey = mSortKey;
|
||||
n.priority = mPriority;
|
||||
if (mActions.size() > 0) {
|
||||
n.actions = new Action[mActions.size()];
|
||||
mActions.toArray(n.actions);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
297
core/java/android/app/RemoteInput.java
Normal file
297
core/java/android/app/RemoteInput.java
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A {@code RemoteInput} object specifies input to be collected from a user to be passed along with
|
||||
* an intent inside a {@link android.app.PendingIntent} that is sent.
|
||||
* Always use {@link RemoteInput.Builder} to create instances of this class.
|
||||
* <p class="note"> See
|
||||
* <a href="{@docRoot}wear/notifications/remote-input.html">Receiving Voice Input from
|
||||
* a Notification</a> for more information on how to use this class.
|
||||
*
|
||||
* <p>The following example adds a {@code RemoteInput} to a {@link Notification.Action},
|
||||
* sets the result key as {@code quick_reply}, and sets the label as {@code Quick reply}.
|
||||
* Users are prompted to input a response when they trigger the action. The results are sent along
|
||||
* with the intent and can be retrieved with the result key (provided to the {@link Builder}
|
||||
* constructor) from the Bundle returned by {@link #getResultsFromIntent}.
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* public static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
|
||||
* Notification.Action action = new Notification.Action.Builder(
|
||||
* R.drawable.reply, "Reply", actionIntent)
|
||||
* <b>.addRemoteInput(new RemoteInput.Builder(KEY_QUICK_REPLY_TEXT)
|
||||
* .setLabel("Quick reply").build()</b>)
|
||||
* .build();</pre>
|
||||
*
|
||||
* <p>When the {@link android.app.PendingIntent} is fired, the intent inside will contain the
|
||||
* input results if collected. To access these results, use the {@link #getResultsFromIntent}
|
||||
* function. The result values will present under the result key passed to the {@link Builder}
|
||||
* constructor.
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* public static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
|
||||
* Bundle results = RemoteInput.getResultsFromIntent(intent);
|
||||
* if (results != null) {
|
||||
* CharSequence quickReplyResult = results.getCharSequence(KEY_QUICK_REPLY_TEXT);
|
||||
* }</pre>
|
||||
*/
|
||||
public final class RemoteInput implements Parcelable {
|
||||
/** Label used to denote the clip data type used for remote input transport */
|
||||
public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
|
||||
|
||||
/** Extra added to a clip data intent object to hold the results bundle. */
|
||||
public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
|
||||
|
||||
private final String mResultKey;
|
||||
private final CharSequence mLabel;
|
||||
private final CharSequence[] mChoices;
|
||||
private final boolean mAllowFreeFormInput;
|
||||
private final Bundle mExtras;
|
||||
|
||||
private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
|
||||
boolean allowFreeFormInput, Bundle extras) {
|
||||
this.mResultKey = resultKey;
|
||||
this.mLabel = label;
|
||||
this.mChoices = choices;
|
||||
this.mAllowFreeFormInput = allowFreeFormInput;
|
||||
this.mExtras = extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key that the result of this input will be set in from the Bundle returned by
|
||||
* {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent.
|
||||
*/
|
||||
public String getResultKey() {
|
||||
return mResultKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label to display to users when collecting this input.
|
||||
*/
|
||||
public CharSequence getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get possible input choices. This can be {@code null} if there are no choices to present.
|
||||
*/
|
||||
public CharSequence[] getChoices() {
|
||||
return mChoices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not users can provide an arbitrary value for
|
||||
* input. If you set this to {@code false}, users must select one of the
|
||||
* choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown
|
||||
* if you set this to false and {@link #getChoices} returns {@code null} or empty.
|
||||
*/
|
||||
public boolean getAllowFreeFormInput() {
|
||||
return mAllowFreeFormInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get additional metadata carried around with this remote input.
|
||||
*/
|
||||
public Bundle getExtras() {
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link RemoteInput} objects.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final String mResultKey;
|
||||
private CharSequence mLabel;
|
||||
private CharSequence[] mChoices;
|
||||
private boolean mAllowFreeFormInput = true;
|
||||
private Bundle mExtras = new Bundle();
|
||||
|
||||
/**
|
||||
* Create a builder object for {@link RemoteInput} objects.
|
||||
* @param resultKey the Bundle key that refers to this input when collected from the user
|
||||
*/
|
||||
public Builder(String resultKey) {
|
||||
if (resultKey == null) {
|
||||
throw new IllegalArgumentException("Result key can't be null");
|
||||
}
|
||||
mResultKey = resultKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a label to be displayed to the user when collecting this input.
|
||||
* @param label The label to show to users when they input a response.
|
||||
* @return this object for method chaining
|
||||
*/
|
||||
public Builder setLabel(CharSequence label) {
|
||||
mLabel = Notification.safeCharSequence(label);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies choices available to the user to satisfy this input.
|
||||
* @param choices an array of pre-defined choices for users input.
|
||||
* You must provide a non-null and non-empty array if
|
||||
* you set {@link #mAllowFreeFormInput} to {@code false}.
|
||||
* @return this object for method chaining
|
||||
*/
|
||||
public Builder setChoices(CharSequence[] choices) {
|
||||
if (choices == null) {
|
||||
mChoices = null;
|
||||
} else {
|
||||
mChoices = new CharSequence[choices.length];
|
||||
for (int i = 0; i < choices.length; i++) {
|
||||
mChoices[i] = Notification.safeCharSequence(choices[i]);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether the user can provide arbitrary values.
|
||||
*
|
||||
* @param allowFreeFormInput The default is {@code true}.
|
||||
* If you specify {@code false}, you must provide a non-null
|
||||
* and non-empty array to {@link #setChoices} or an
|
||||
* {@link IllegalArgumentException} is thrown.
|
||||
* @return this object for method chaining
|
||||
*/
|
||||
public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
|
||||
mAllowFreeFormInput = allowFreeFormInput;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge additional metadata into this builder.
|
||||
*
|
||||
* <p>Values within the Bundle will replace existing extras values in this Builder.
|
||||
*
|
||||
* @see RemoteInput#getExtras
|
||||
*/
|
||||
public Builder addExtras(Bundle extras) {
|
||||
if (extras != null) {
|
||||
mExtras.putAll(extras);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metadata Bundle used by this Builder.
|
||||
*
|
||||
* <p>The returned Bundle is shared with this Builder.
|
||||
*/
|
||||
public Bundle getExtras() {
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all of the options that have been set and return a new {@link RemoteInput}
|
||||
* object.
|
||||
*/
|
||||
public RemoteInput build() {
|
||||
return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteInput(Parcel in) {
|
||||
mResultKey = in.readString();
|
||||
mLabel = in.readCharSequence();
|
||||
mChoices = in.readCharSequenceArray();
|
||||
mAllowFreeFormInput = in.readInt() != 0;
|
||||
mExtras = in.readBundle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remote input results bundle from an intent. The returned Bundle will
|
||||
* contain a key/value for every result key populated by remote input collector.
|
||||
* Use the {@link Bundle#getCharSequence(String)} method to retrieve a value.
|
||||
* @param intent The intent object that fired in response to an action or content intent
|
||||
* which also had one or more remote input requested.
|
||||
*/
|
||||
public static Bundle getResultsFromIntent(Intent intent) {
|
||||
ClipData clipData = intent.getClipData();
|
||||
if (clipData == null) {
|
||||
return null;
|
||||
}
|
||||
ClipDescription clipDescription = clipData.getDescription();
|
||||
if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
|
||||
return null;
|
||||
}
|
||||
if (clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
|
||||
return clipData.getItemAt(0).getIntent().getExtras().getParcelable(EXTRA_RESULTS_DATA);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate an intent object with the results gathered from remote input. This method
|
||||
* should only be called by remote input collection services when sending results to a
|
||||
* pending intent.
|
||||
* @param remoteInputs The remote inputs for which results are being provided
|
||||
* @param intent The intent to add remote inputs to. The {@link ClipData}
|
||||
* field of the intent will be modified to contain the results.
|
||||
* @param results A bundle holding the remote input results. This bundle should
|
||||
* be populated with keys matching the result keys specified in
|
||||
* {@code remoteInputs} with values being the result per key.
|
||||
*/
|
||||
public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
|
||||
Bundle results) {
|
||||
Bundle resultsBundle = new Bundle();
|
||||
for (RemoteInput remoteInput : remoteInputs) {
|
||||
Object result = results.get(remoteInput.getResultKey());
|
||||
if (result instanceof CharSequence) {
|
||||
resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
|
||||
}
|
||||
}
|
||||
Intent clipIntent = new Intent();
|
||||
clipIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
|
||||
intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipIntent));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(mResultKey);
|
||||
out.writeCharSequence(mLabel);
|
||||
out.writeCharSequenceArray(mChoices);
|
||||
out.writeInt(mAllowFreeFormInput ? 1 : 0);
|
||||
out.writeBundle(mExtras);
|
||||
}
|
||||
|
||||
public static final Creator<RemoteInput> CREATOR = new Creator<RemoteInput>() {
|
||||
@Override
|
||||
public RemoteInput createFromParcel(Parcel in) {
|
||||
return new RemoteInput(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteInput[] newArray(int size) {
|
||||
return new RemoteInput[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user