diff options
author | Griff Hazen <griff@google.com> | 2014-05-20 09:55:39 -0700 |
---|---|---|
committer | Griff Hazen <griff@google.com> | 2014-05-20 09:55:39 -0700 |
commit | 5cadc3b00aa775a63518383046c902b130e09b4c (patch) | |
tree | 7283af9b6305b27fbb22033a40f90e3586fa1557 /core/java/android/app/RemoteInput.java | |
parent | e0d6a6b05b0861a5aa6dd8189de61a4da8d39dae (diff) | |
download | frameworks_base-5cadc3b00aa775a63518383046c902b130e09b4c.zip frameworks_base-5cadc3b00aa775a63518383046c902b130e09b4c.tar.gz frameworks_base-5cadc3b00aa775a63518383046c902b130e09b4c.tar.bz2 |
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
Diffstat (limited to 'core/java/android/app/RemoteInput.java')
-rw-r--r-- | core/java/android/app/RemoteInput.java | 297 |
1 files changed, 297 insertions, 0 deletions
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]; + } + }; +} |