blob: 85fe99d95969bb4935cff831a48405a0c378afae [file] [log] [blame]
Griff Hazen5cadc3b2014-05-20 09:55:39 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
Petr Cermak8e2731b2018-03-16 10:23:00 +000019import android.annotation.IntDef;
Kodlee Yin4b3a5472018-05-09 16:19:56 -070020import android.annotation.NonNull;
21import android.annotation.Nullable;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070022import android.content.ClipData;
23import android.content.ClipDescription;
24import android.content.Intent;
Shane Brennan472a3b32016-12-12 15:28:10 -080025import android.net.Uri;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070026import android.os.Bundle;
27import android.os.Parcel;
28import android.os.Parcelable;
Shane Brennan472a3b32016-12-12 15:28:10 -080029import android.util.ArraySet;
Petr Cermak6cb667c2018-01-16 15:28:02 +000030
Petr Cermak8e2731b2018-03-16 10:23:00 +000031import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
Shane Brennan472a3b32016-12-12 15:28:10 -080033import java.util.HashMap;
34import java.util.Map;
35import java.util.Set;
Griff Hazen5cadc3b2014-05-20 09:55:39 -070036
37/**
38 * A {@code RemoteInput} object specifies input to be collected from a user to be passed along with
39 * an intent inside a {@link android.app.PendingIntent} that is sent.
40 * Always use {@link RemoteInput.Builder} to create instances of this class.
41 * <p class="note"> See
Cindy Kuangedc2f842017-08-15 13:30:00 -070042 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#direct">Replying
43 * to notifications</a> for more information on how to use this class.
Griff Hazen5cadc3b2014-05-20 09:55:39 -070044 *
45 * <p>The following example adds a {@code RemoteInput} to a {@link Notification.Action},
46 * sets the result key as {@code quick_reply}, and sets the label as {@code Quick reply}.
47 * Users are prompted to input a response when they trigger the action. The results are sent along
48 * with the intent and can be retrieved with the result key (provided to the {@link Builder}
49 * constructor) from the Bundle returned by {@link #getResultsFromIntent}.
50 *
51 * <pre class="prettyprint">
52 * public static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
53 * Notification.Action action = new Notification.Action.Builder(
54 * R.drawable.reply, &quot;Reply&quot;, actionIntent)
55 * <b>.addRemoteInput(new RemoteInput.Builder(KEY_QUICK_REPLY_TEXT)
56 * .setLabel("Quick reply").build()</b>)
57 * .build();</pre>
58 *
59 * <p>When the {@link android.app.PendingIntent} is fired, the intent inside will contain the
60 * input results if collected. To access these results, use the {@link #getResultsFromIntent}
61 * function. The result values will present under the result key passed to the {@link Builder}
62 * constructor.
63 *
64 * <pre class="prettyprint">
65 * public static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
66 * Bundle results = RemoteInput.getResultsFromIntent(intent);
67 * if (results != null) {
68 * CharSequence quickReplyResult = results.getCharSequence(KEY_QUICK_REPLY_TEXT);
69 * }</pre>
70 */
71public final class RemoteInput implements Parcelable {
72 /** Label used to denote the clip data type used for remote input transport */
73 public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
74
Shane Brennan472a3b32016-12-12 15:28:10 -080075 /** Extra added to a clip data intent object to hold the text results bundle. */
Griff Hazen5cadc3b2014-05-20 09:55:39 -070076 public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
77
Shane Brennan472a3b32016-12-12 15:28:10 -080078 /** Extra added to a clip data intent object to hold the data results bundle. */
79 private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
80 "android.remoteinput.dataTypeResultsData";
81
Petr Cermak8e2731b2018-03-16 10:23:00 +000082 /** Extra added to a clip data intent object identifying the {@link Source} of the results. */
Petr Cermak6cb667c2018-01-16 15:28:02 +000083 private static final String EXTRA_RESULTS_SOURCE = "android.remoteinput.resultsSource";
84
Petr Cermak8e2731b2018-03-16 10:23:00 +000085 /** @hide */
86 @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_FREE_FORM_INPUT, SOURCE_CHOICE})
87 @Retention(RetentionPolicy.SOURCE)
88 public @interface Source {}
89
Petr Cermak6cb667c2018-01-16 15:28:02 +000090 /** The user manually entered the data. */
91 public static final int SOURCE_FREE_FORM_INPUT = 0;
92
93 /** The user selected one of the choices from {@link #getChoices}. */
94 public static final int SOURCE_CHOICE = 1;
95
Griff Hazenc3104152014-05-22 14:38:36 -070096 // Flags bitwise-ored to mFlags
97 private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
98
99 // Default value for flags integer
100 private static final int DEFAULT_FLAGS = FLAG_ALLOW_FREE_FORM_INPUT;
101
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700102 private final String mResultKey;
103 private final CharSequence mLabel;
104 private final CharSequence[] mChoices;
Griff Hazenc3104152014-05-22 14:38:36 -0700105 private final int mFlags;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700106 private final Bundle mExtras;
Shane Brennan472a3b32016-12-12 15:28:10 -0800107 private final ArraySet<String> mAllowedDataTypes;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700108
109 private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
Shane Brennan472a3b32016-12-12 15:28:10 -0800110 int flags, Bundle extras, ArraySet<String> allowedDataTypes) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700111 this.mResultKey = resultKey;
112 this.mLabel = label;
113 this.mChoices = choices;
Griff Hazenc3104152014-05-22 14:38:36 -0700114 this.mFlags = flags;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700115 this.mExtras = extras;
Shane Brennan472a3b32016-12-12 15:28:10 -0800116 this.mAllowedDataTypes = allowedDataTypes;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700117 }
118
119 /**
120 * Get the key that the result of this input will be set in from the Bundle returned by
121 * {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent.
122 */
123 public String getResultKey() {
124 return mResultKey;
125 }
126
127 /**
128 * Get the label to display to users when collecting this input.
129 */
130 public CharSequence getLabel() {
131 return mLabel;
132 }
133
134 /**
135 * Get possible input choices. This can be {@code null} if there are no choices to present.
136 */
137 public CharSequence[] getChoices() {
138 return mChoices;
139 }
140
Shane Brennanfa458a22017-04-26 12:27:53 -0700141 /**
142 * Get possible non-textual inputs that are accepted.
143 * This can be {@code null} if the input does not accept non-textual values.
144 * See {@link Builder#setAllowDataType}.
145 */
Shane Brennan472a3b32016-12-12 15:28:10 -0800146 public Set<String> getAllowedDataTypes() {
147 return mAllowedDataTypes;
148 }
149
150 /**
151 * Returns true if the input only accepts data, meaning {@link #getAllowFreeFormInput}
152 * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes is
153 * non-null and not empty.
154 */
155 public boolean isDataOnly() {
156 return !getAllowFreeFormInput()
157 && (getChoices() == null || getChoices().length == 0)
158 && !getAllowedDataTypes().isEmpty();
159 }
160
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700161 /**
162 * Get whether or not users can provide an arbitrary value for
163 * input. If you set this to {@code false}, users must select one of the
164 * choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown
165 * if you set this to false and {@link #getChoices} returns {@code null} or empty.
166 */
167 public boolean getAllowFreeFormInput() {
Griff Hazenc3104152014-05-22 14:38:36 -0700168 return (mFlags & FLAG_ALLOW_FREE_FORM_INPUT) != 0;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700169 }
170
171 /**
172 * Get additional metadata carried around with this remote input.
173 */
174 public Bundle getExtras() {
175 return mExtras;
176 }
177
178 /**
179 * Builder class for {@link RemoteInput} objects.
180 */
181 public static final class Builder {
182 private final String mResultKey;
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700183 private final ArraySet<String> mAllowedDataTypes = new ArraySet<>();
184 private final Bundle mExtras = new Bundle();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700185 private CharSequence mLabel;
186 private CharSequence[] mChoices;
Griff Hazenc3104152014-05-22 14:38:36 -0700187 private int mFlags = DEFAULT_FLAGS;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700188
189 /**
190 * Create a builder object for {@link RemoteInput} objects.
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700191 *
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700192 * @param resultKey the Bundle key that refers to this input when collected from the user
193 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700194 public Builder(@NonNull String resultKey) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700195 if (resultKey == null) {
196 throw new IllegalArgumentException("Result key can't be null");
197 }
198 mResultKey = resultKey;
199 }
200
201 /**
202 * Set a label to be displayed to the user when collecting this input.
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700203 *
204 * @param label The label to show to users when they input a response
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700205 * @return this object for method chaining
206 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700207 @NonNull
208 public Builder setLabel(@Nullable CharSequence label) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700209 mLabel = Notification.safeCharSequence(label);
210 return this;
211 }
212
213 /**
214 * Specifies choices available to the user to satisfy this input.
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700215 *
216 * <p>Note: Starting in Android P, these choices will always be shown on phones if the app's
217 * target SDK is >= P. However, these choices may also be rendered on other types of devices
218 * regardless of target SDK.
219 *
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700220 * @param choices an array of pre-defined choices for users input.
221 * You must provide a non-null and non-empty array if
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700222 * you disabled free form input using {@link #setAllowFreeFormInput}
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700223 * @return this object for method chaining
224 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700225 @NonNull
226 public Builder setChoices(@Nullable CharSequence[] choices) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700227 if (choices == null) {
228 mChoices = null;
229 } else {
230 mChoices = new CharSequence[choices.length];
231 for (int i = 0; i < choices.length; i++) {
232 mChoices[i] = Notification.safeCharSequence(choices[i]);
233 }
234 }
235 return this;
236 }
237
238 /**
Shane Brennanfa458a22017-04-26 12:27:53 -0700239 * Specifies whether the user can provide arbitrary values. This allows an input
240 * to accept non-textual values. Examples of usage are an input that wants audio
241 * or an image.
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700242 *
Shane Brennan472a3b32016-12-12 15:28:10 -0800243 * @param mimeType A mime type that results are allowed to come in.
244 * Be aware that text results (see {@link #setAllowFreeFormInput}
245 * are allowed by default. If you do not want text results you will have to
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700246 * pass false to {@code setAllowFreeFormInput}
247 * @param doAllow Whether the mime type should be allowed or not
Shane Brennan472a3b32016-12-12 15:28:10 -0800248 * @return this object for method chaining
249 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700250 @NonNull
251 public Builder setAllowDataType(@NonNull String mimeType, boolean doAllow) {
Shane Brennan472a3b32016-12-12 15:28:10 -0800252 if (doAllow) {
253 mAllowedDataTypes.add(mimeType);
254 } else {
255 mAllowedDataTypes.remove(mimeType);
256 }
257 return this;
258 }
259
260 /**
261 * Specifies whether the user can provide arbitrary text values.
262 *
263 * @param allowFreeFormTextInput The default is {@code true}.
264 * If you specify {@code false}, you must either provide a non-null
265 * and non-empty array to {@link #setChoices}, or enable a data result
266 * in {@code setAllowDataType}. Otherwise an
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700267 * {@link IllegalArgumentException} is thrown
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700268 * @return this object for method chaining
269 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700270 @NonNull
Shane Brennan472a3b32016-12-12 15:28:10 -0800271 public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
272 setFlag(mFlags, allowFreeFormTextInput);
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700273 return this;
274 }
275
276 /**
277 * Merge additional metadata into this builder.
278 *
279 * <p>Values within the Bundle will replace existing extras values in this Builder.
280 *
281 * @see RemoteInput#getExtras
282 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700283 @NonNull
284 public Builder addExtras(@NonNull Bundle extras) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700285 if (extras != null) {
286 mExtras.putAll(extras);
287 }
288 return this;
289 }
290
291 /**
292 * Get the metadata Bundle used by this Builder.
293 *
294 * <p>The returned Bundle is shared with this Builder.
295 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700296 @NonNull
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700297 public Bundle getExtras() {
298 return mExtras;
299 }
300
Griff Hazenc3104152014-05-22 14:38:36 -0700301 private void setFlag(int mask, boolean value) {
302 if (value) {
303 mFlags |= mask;
304 } else {
305 mFlags &= ~mask;
306 }
307 }
308
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700309 /**
310 * Combine all of the options that have been set and return a new {@link RemoteInput}
311 * object.
312 */
Kodlee Yin4b3a5472018-05-09 16:19:56 -0700313 @NonNull
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700314 public RemoteInput build() {
Shane Brennan472a3b32016-12-12 15:28:10 -0800315 return new RemoteInput(
316 mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700317 }
318 }
319
320 private RemoteInput(Parcel in) {
321 mResultKey = in.readString();
322 mLabel = in.readCharSequence();
323 mChoices = in.readCharSequenceArray();
Griff Hazenc3104152014-05-22 14:38:36 -0700324 mFlags = in.readInt();
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700325 mExtras = in.readBundle();
Shane Brennan472a3b32016-12-12 15:28:10 -0800326 mAllowedDataTypes = (ArraySet<String>) in.readArraySet(null);
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700327 }
328
329 /**
Shane Brennan472a3b32016-12-12 15:28:10 -0800330 * Similar as {@link #getResultsFromIntent} but retrieves data results for a
331 * specific RemoteInput result. To retrieve a value use:
332 * <pre>
333 * {@code
334 * Map<String, Uri> results =
335 * RemoteInput.getDataResultsFromIntent(intent, REMOTE_INPUT_KEY);
336 * if (results != null) {
337 * Uri data = results.get(MIME_TYPE_OF_INTEREST);
338 * }
339 * }
340 * </pre>
341 * @param intent The intent object that fired in response to an action or content intent
342 * which also had one or more remote input requested.
343 * @param remoteInputResultKey The result key for the RemoteInput you want results for.
344 */
345 public static Map<String, Uri> getDataResultsFromIntent(
346 Intent intent, String remoteInputResultKey) {
347 Intent clipDataIntent = getClipDataIntentFromIntent(intent);
348 if (clipDataIntent == null) {
349 return null;
350 }
351 Map<String, Uri> results = new HashMap<>();
352 Bundle extras = clipDataIntent.getExtras();
353 for (String key : extras.keySet()) {
354 if (key.startsWith(EXTRA_DATA_TYPE_RESULTS_DATA)) {
355 String mimeType = key.substring(EXTRA_DATA_TYPE_RESULTS_DATA.length());
356 if (mimeType == null || mimeType.isEmpty()) {
357 continue;
358 }
359 Bundle bundle = clipDataIntent.getBundleExtra(key);
360 String uriStr = bundle.getString(remoteInputResultKey);
361 if (uriStr == null || uriStr.isEmpty()) {
362 continue;
363 }
364 results.put(mimeType, Uri.parse(uriStr));
365 }
366 }
367 return results.isEmpty() ? null : results;
368 }
369
370 /**
371 * Get the remote input text results bundle from an intent. The returned Bundle will
372 * contain a key/value for every result key populated with text by remote input collector.
373 * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value. For non-text
374 * results use {@link #getDataResultsFromIntent}.
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700375 * @param intent The intent object that fired in response to an action or content intent
376 * which also had one or more remote input requested.
377 */
378 public static Bundle getResultsFromIntent(Intent intent) {
Shane Brennan472a3b32016-12-12 15:28:10 -0800379 Intent clipDataIntent = getClipDataIntentFromIntent(intent);
380 if (clipDataIntent == null) {
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700381 return null;
382 }
Shane Brennan472a3b32016-12-12 15:28:10 -0800383 return clipDataIntent.getExtras().getParcelable(EXTRA_RESULTS_DATA);
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700384 }
385
386 /**
Shane Brennan472a3b32016-12-12 15:28:10 -0800387 * Populate an intent object with the text results gathered from remote input. This method
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700388 * should only be called by remote input collection services when sending results to a
389 * pending intent.
390 * @param remoteInputs The remote inputs for which results are being provided
391 * @param intent The intent to add remote inputs to. The {@link ClipData}
392 * field of the intent will be modified to contain the results.
393 * @param results A bundle holding the remote input results. This bundle should
394 * be populated with keys matching the result keys specified in
Shane Brennan472a3b32016-12-12 15:28:10 -0800395 * {@code remoteInputs} with values being the CharSequence results per key.
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700396 */
397 public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
398 Bundle results) {
Shane Brennan472a3b32016-12-12 15:28:10 -0800399 Intent clipDataIntent = getClipDataIntentFromIntent(intent);
400 if (clipDataIntent == null) {
401 clipDataIntent = new Intent(); // First time we've added a result.
402 }
403 Bundle resultsBundle = clipDataIntent.getBundleExtra(EXTRA_RESULTS_DATA);
404 if (resultsBundle == null) {
405 resultsBundle = new Bundle();
406 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700407 for (RemoteInput remoteInput : remoteInputs) {
408 Object result = results.get(remoteInput.getResultKey());
409 if (result instanceof CharSequence) {
410 resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
411 }
412 }
Shane Brennan472a3b32016-12-12 15:28:10 -0800413 clipDataIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
414 intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
415 }
416
417 /**
Shane Brennanfa458a22017-04-26 12:27:53 -0700418 * Same as {@link #addResultsToIntent} but for setting data results. This is used
419 * for inputs that accept non-textual results (see {@link Builder#setAllowDataType}).
420 * Only one result can be provided for every mime type accepted by the RemoteInput.
421 * If multiple inputs of the same mime type are expected then multiple RemoteInputs
422 * should be used.
423 *
Shane Brennan472a3b32016-12-12 15:28:10 -0800424 * @param remoteInput The remote input for which results are being provided
425 * @param intent The intent to add remote input results to. The {@link ClipData}
426 * field of the intent will be modified to contain the results.
427 * @param results A map of mime type to the Uri result for that mime type.
428 */
429 public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
430 Map<String, Uri> results) {
431 Intent clipDataIntent = getClipDataIntentFromIntent(intent);
432 if (clipDataIntent == null) {
433 clipDataIntent = new Intent(); // First time we've added a result.
434 }
435 for (Map.Entry<String, Uri> entry : results.entrySet()) {
436 String mimeType = entry.getKey();
437 Uri uri = entry.getValue();
438 if (mimeType == null) {
439 continue;
440 }
441 Bundle resultsBundle =
442 clipDataIntent.getBundleExtra(getExtraResultsKeyForData(mimeType));
443 if (resultsBundle == null) {
444 resultsBundle = new Bundle();
445 }
446 resultsBundle.putString(remoteInput.getResultKey(), uri.toString());
447
448 clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle);
449 }
450 intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
451 }
452
Petr Cermak6cb667c2018-01-16 15:28:02 +0000453 /**
454 * Set the source of the RemoteInput results. This method should only be called by remote
455 * input collection services (e.g.
456 * {@link android.service.notification.NotificationListenerService})
457 * when sending results to a pending intent.
458 *
459 * @see #SOURCE_FREE_FORM_INPUT
460 * @see #SOURCE_CHOICE
461 *
462 * @param intent The intent to add remote input source to. The {@link ClipData}
463 * field of the intent will be modified to contain the source.
Petr Cermak6cb667c2018-01-16 15:28:02 +0000464 * @param source The source of the results.
465 */
Petr Cermak8e2731b2018-03-16 10:23:00 +0000466 public static void setResultsSource(Intent intent, @Source int source) {
Petr Cermak6cb667c2018-01-16 15:28:02 +0000467 Intent clipDataIntent = getClipDataIntentFromIntent(intent);
468 if (clipDataIntent == null) {
469 clipDataIntent = new Intent(); // First time we've added a result.
470 }
471 clipDataIntent.putExtra(EXTRA_RESULTS_SOURCE, source);
472 intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
473 }
474
475 /**
476 * Get the source of the RemoteInput results.
477 *
478 * @see #SOURCE_FREE_FORM_INPUT
479 * @see #SOURCE_CHOICE
480 *
481 * @param intent The intent object that fired in response to an action or content intent
482 * which also had one or more remote input requested.
483 * @return The source of the results. If no source was set, {@link #SOURCE_FREE_FORM_INPUT} will
484 * be returned.
485 */
Petr Cermak8e2731b2018-03-16 10:23:00 +0000486 @Source
Petr Cermak6cb667c2018-01-16 15:28:02 +0000487 public static int getResultsSource(Intent intent) {
488 Intent clipDataIntent = getClipDataIntentFromIntent(intent);
489 if (clipDataIntent == null) {
490 return SOURCE_FREE_FORM_INPUT;
491 }
492 return clipDataIntent.getExtras().getInt(EXTRA_RESULTS_SOURCE, SOURCE_FREE_FORM_INPUT);
493 }
494
Shane Brennan472a3b32016-12-12 15:28:10 -0800495 private static String getExtraResultsKeyForData(String mimeType) {
496 return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700497 }
498
499 @Override
500 public int describeContents() {
501 return 0;
502 }
503
504 @Override
505 public void writeToParcel(Parcel out, int flags) {
506 out.writeString(mResultKey);
507 out.writeCharSequence(mLabel);
508 out.writeCharSequenceArray(mChoices);
Griff Hazenc3104152014-05-22 14:38:36 -0700509 out.writeInt(mFlags);
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700510 out.writeBundle(mExtras);
Shane Brennan472a3b32016-12-12 15:28:10 -0800511 out.writeArraySet(mAllowedDataTypes);
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700512 }
513
514 public static final Creator<RemoteInput> CREATOR = new Creator<RemoteInput>() {
515 @Override
516 public RemoteInput createFromParcel(Parcel in) {
517 return new RemoteInput(in);
518 }
519
520 @Override
521 public RemoteInput[] newArray(int size) {
522 return new RemoteInput[size];
523 }
524 };
Shane Brennan472a3b32016-12-12 15:28:10 -0800525
526 private static Intent getClipDataIntentFromIntent(Intent intent) {
527 ClipData clipData = intent.getClipData();
528 if (clipData == null) {
529 return null;
530 }
531 ClipDescription clipDescription = clipData.getDescription();
532 if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
533 return null;
534 }
535 if (!clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
536 return null;
537 }
538 return clipData.getItemAt(0).getIntent();
539 }
Griff Hazen5cadc3b2014-05-20 09:55:39 -0700540}