resolved conflicts for merge of 8f1e7ade to master
Change-Id: Icabc5d274502abf8d40116d373c9f9d0ac062386
diff --git a/api/current.txt b/api/current.txt
index 86d1457..9ac27db 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4424,6 +4424,8 @@
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 java.lang.String CATEGORY_ALARM = "alarm";
@@ -4466,6 +4468,7 @@
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
@@ -4516,6 +4519,7 @@
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;
@@ -4523,14 +4527,20 @@
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);
@@ -4554,6 +4564,7 @@
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 addPerson(java.lang.String);
+ 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();
@@ -4569,6 +4580,8 @@
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);
@@ -4581,6 +4594,7 @@
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);
@@ -4593,6 +4607,10 @@
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);
@@ -4696,6 +4714,31 @@
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);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fd76b9c4..20ad46e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -45,6 +45,7 @@
import java.lang.annotation.RetentionPolicy;
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
@@ -370,6 +371,14 @@
*/
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;
/** @hide */
@@ -539,6 +548,34 @@
*/
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>
@@ -706,15 +743,18 @@
*/
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.
@@ -728,19 +768,23 @@
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;
}
/**
@@ -751,13 +795,22 @@
}
/**
+ * 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.
@@ -766,7 +819,7 @@
* @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);
}
/**
@@ -775,14 +828,20 @@
* @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);
+ }
}
/**
@@ -809,22 +868,62 @@
}
/**
+ * 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() {
@@ -841,6 +940,7 @@
out.writeInt(0);
}
out.writeBundle(mExtras);
+ out.writeTypedArray(mRemoteInputs, flags);
}
public static final Parcelable.Creator<Action> CREATOR =
new Parcelable.Creator<Action>() {
@@ -960,6 +1060,10 @@
category = parcel.readString();
+ mGroupKey = parcel.readString();
+
+ mSortKey = parcel.readString();
+
extras = parcel.readBundle(); // may be null
actions = parcel.createTypedArray(Action.CREATOR); // may be null
@@ -1037,6 +1141,10 @@
that.category = this.category;
+ that.mGroupKey = this.mGroupKey;
+
+ that.mSortKey = this.mSortKey;
+
if (this.extras != null) {
try {
that.extras = new Bundle(this.extras);
@@ -1188,6 +1296,10 @@
parcel.writeString(category);
+ parcel.writeString(mGroupKey);
+
+ parcel.writeString(mSortKey);
+
parcel.writeBundle(extras); // null ok
parcel.writeTypedArray(actions, 0); // null ok
@@ -1325,7 +1437,18 @@
sb.append(" flags=0x");
sb.append(Integer.toHexString(this.flags));
sb.append(String.format(" color=0x%08x", this.color));
- 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);
@@ -1408,6 +1531,8 @@
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);
@@ -1839,6 +1964,51 @@
}
/**
+ * 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.
*
* <p>Values within the Bundle will replace existing extras values in this Builder.
@@ -1949,7 +2119,7 @@
/**
* Specify the value of {@link #visibility}.
-
+ *
* @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
* {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
*
@@ -1971,6 +2141,28 @@
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;
@@ -2298,6 +2490,8 @@
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()];
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
new file mode 100644
index 0000000..098568e
--- /dev/null
+++ b/core/java/android/app/RemoteInput.java
@@ -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];
+ }
+ };
+}