blob: 90b58aae5752deb6f625fa678fae4a12fb3ad9f6 [file] [log] [blame]
Dianne Hackborn9f531192010-08-04 17:48:03 -07001/**
2 * Copyright (c) 2010, 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.content;
18
Nicolas Prevotd85fc722014-04-16 19:52:08 +010019import static android.content.ContentProvider.maybeAddUserId;
Vladislav Kaznacheevc14df8e2016-01-22 11:49:13 -080020
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070021import android.content.res.AssetFileDescriptor;
Dianne Hackborn9f531192010-08-04 17:48:03 -070022import android.graphics.Bitmap;
23import android.net.Uri;
24import android.os.Parcel;
25import android.os.Parcelable;
Jeff Sharkeya14acd22013-04-02 18:27:45 -070026import android.os.StrictMode;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -070027import android.text.Html;
28import android.text.Spannable;
29import android.text.SpannableStringBuilder;
30import android.text.Spanned;
Dianne Hackborn9f531192010-08-04 17:48:03 -070031import android.text.TextUtils;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -070032import android.text.style.URLSpan;
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070033import android.util.Log;
Dianne Hackborn9f531192010-08-04 17:48:03 -070034
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070035import java.io.FileInputStream;
36import java.io.FileNotFoundException;
37import java.io.IOException;
38import java.io.InputStreamReader;
Dianne Hackborn9f531192010-08-04 17:48:03 -070039import java.util.ArrayList;
Vladislav Kaznacheev9149d2b2015-12-15 12:16:28 -080040import java.util.List;
Dianne Hackborn9f531192010-08-04 17:48:03 -070041
42/**
43 * Representation of a clipped data on the clipboard.
44 *
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -070045 * <p>ClipData is a complex type containing one or more Item instances,
Dianne Hackborn9f531192010-08-04 17:48:03 -070046 * each of which can hold one or more representations of an item of data.
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -070047 * For display to the user, it also has a label.</p>
Dianne Hackborn9f531192010-08-04 17:48:03 -070048 *
Dianne Hackbornf834dfa2010-10-26 12:43:57 -070049 * <p>A ClipData contains a {@link ClipDescription}, which describes
50 * important meta-data about the clip. In particular, its
51 * {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)}
Dianne Hackborn1040dc42010-08-26 22:11:06 -070052 * must return correct MIME type(s) describing the data in the clip. For help
53 * in correctly constructing a clip with the correct MIME type, use
Dianne Hackborn327fbd22011-01-17 14:38:50 -080054 * {@link #newPlainText(CharSequence, CharSequence)},
55 * {@link #newUri(ContentResolver, CharSequence, Uri)}, and
56 * {@link #newIntent(CharSequence, Intent)}.
Dianne Hackborn1040dc42010-08-26 22:11:06 -070057 *
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070058 * <p>Each Item instance can be one of three main classes of data: a simple
59 * CharSequence of text, a single Intent object, or a Uri. See {@link Item}
60 * for more details.
Dianne Hackborn9f531192010-08-04 17:48:03 -070061 *
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080062 * <div class="special reference">
63 * <h3>Developer Guides</h3>
64 * <p>For more information about using the clipboard framework, read the
65 * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a>
66 * developer guide.</p>
67 * </div>
68 *
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070069 * <a name="ImplementingPaste"></a>
70 * <h3>Implementing Paste or Drop</h3>
71 *
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -070072 * <p>To implement a paste or drop of a ClipData object into an application,
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070073 * the application must correctly interpret the data for its use. If the {@link Item}
74 * it contains is simple text or an Intent, there is little to be done: text
75 * can only be interpreted as text, and an Intent will typically be used for
76 * creating shortcuts (such as placing icons on the home screen) or other
77 * actions.
78 *
79 * <p>If all you want is the textual representation of the clipped data, you
80 * can use the convenience method {@link Item#coerceToText Item.coerceToText}.
Dianne Hackborn1040dc42010-08-26 22:11:06 -070081 * In this case there is generally no need to worry about the MIME types
Dianne Hackbornf834dfa2010-10-26 12:43:57 -070082 * reported by {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)},
Newton Allen4465d1a2013-11-26 10:25:38 -080083 * since any clip item can always be converted to a string.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070084 *
85 * <p>More complicated exchanges will be done through URIs, in particular
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -070086 * "content:" URIs. A content URI allows the recipient of a ClipData item
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070087 * to interact closely with the ContentProvider holding the data in order to
Dianne Hackborn1040dc42010-08-26 22:11:06 -070088 * negotiate the transfer of that data. The clip must also be filled in with
Dianne Hackborn327fbd22011-01-17 14:38:50 -080089 * the available MIME types; {@link #newUri(ContentResolver, CharSequence, Uri)}
Dianne Hackborn1040dc42010-08-26 22:11:06 -070090 * will take care of correctly doing this.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -070091 *
92 * <p>For example, here is the paste function of a simple NotePad application.
93 * When retrieving the data from the clipboard, it can do either two things:
94 * if the clipboard contains a URI reference to an existing note, it copies
95 * the entire structure of the note into a new note; otherwise, it simply
96 * coerces the clip into text and uses that as the new note's contents.
97 *
98 * {@sample development/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
99 * paste}
100 *
101 * <p>In many cases an application can paste various types of streams of data. For
102 * example, an e-mail application may want to allow the user to paste an image
103 * or other binary data as an attachment. This is accomplished through the
104 * ContentResolver {@link ContentResolver#getStreamTypes(Uri, String)} and
105 * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, android.os.Bundle)}
106 * methods. These allow a client to discover the type(s) of data that a particular
107 * content URI can make available as a stream and retrieve the stream of data.
108 *
109 * <p>For example, the implementation of {@link Item#coerceToText Item.coerceToText}
110 * itself uses this to try to retrieve a URI clip as a stream of text:
111 *
Dianne Hackbornf6d952b2010-08-27 15:41:13 -0700112 * {@sample frameworks/base/core/java/android/content/ClipData.java coerceToText}
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700113 *
114 * <a name="ImplementingCopy"></a>
115 * <h3>Implementing Copy or Drag</h3>
116 *
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -0700117 * <p>To be the source of a clip, the application must construct a ClipData
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700118 * object that any recipient can interpret best for their context. If the clip
119 * is to contain a simple text, Intent, or URI, this is easy: an {@link Item}
120 * containing the appropriate data type can be constructed and used.
121 *
122 * <p>More complicated data types require the implementation of support in
123 * a ContentProvider for describing and generating the data for the recipient.
124 * A common scenario is one where an application places on the clipboard the
125 * content: URI of an object that the user has copied, with the data at that
126 * URI consisting of a complicated structure that only other applications with
127 * direct knowledge of the structure can use.
128 *
129 * <p>For applications that do not have intrinsic knowledge of the data structure,
130 * the content provider holding it can make the data available as an arbitrary
131 * number of types of data streams. This is done by implementing the
132 * ContentProvider {@link ContentProvider#getStreamTypes(Uri, String)} and
133 * {@link ContentProvider#openTypedAssetFile(Uri, String, android.os.Bundle)}
134 * methods.
135 *
136 * <p>Going back to our simple NotePad application, this is the implementation
137 * it may have to convert a single note URI (consisting of a title and the note
138 * text) into a stream of plain text data.
139 *
140 * {@sample development/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
141 * stream}
142 *
143 * <p>The copy operation in our NotePad application is now just a simple matter
144 * of making a clip containing the URI of the note being copied:
145 *
146 * {@sample development/samples/NotePad/src/com/example/android/notepad/NotesList.java
147 * copy}
148 *
149 * <p>Note if a paste operation needs this clip as text (for example to paste
150 * into an editor), then {@link Item#coerceToText(Context)} will ask the content
151 * provider for the clip URI as text and successfully paste the entire note.
Dianne Hackborn9f531192010-08-04 17:48:03 -0700152 */
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700153public class ClipData implements Parcelable {
154 static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
155 ClipDescription.MIMETYPE_TEXT_PLAIN };
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700156 static final String[] MIMETYPES_TEXT_HTML = new String[] {
157 ClipDescription.MIMETYPE_TEXT_HTML };
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700158 static final String[] MIMETYPES_TEXT_URILIST = new String[] {
159 ClipDescription.MIMETYPE_TEXT_URILIST };
160 static final String[] MIMETYPES_TEXT_INTENT = new String[] {
161 ClipDescription.MIMETYPE_TEXT_INTENT };
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700162
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700163 final ClipDescription mClipDescription;
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -0700164
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700165 final Bitmap mIcon;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700166
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800167 final ArrayList<Item> mItems;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700168
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700169 /**
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -0700170 * Description of a single item in a ClipData.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700171 *
172 * <p>The types than an individual item can currently contain are:</p>
173 *
174 * <ul>
175 * <li> Text: a basic string of text. This is actually a CharSequence,
176 * so it can be formatted text supported by corresponding Android built-in
177 * style spans. (Custom application spans are not supported and will be
178 * stripped when transporting through the clipboard.)
179 * <li> Intent: an arbitrary Intent object. A typical use is the shortcut
180 * to create when pasting a clipped item on to the home screen.
181 * <li> Uri: a URI reference. This may be any URI (such as an http: URI
182 * representing a bookmark), however it is often a content: URI. Using
183 * content provider references as clips like this allows an application to
184 * share complex or large clips through the standard content provider
185 * facilities.
186 * </ul>
187 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700188 public static class Item {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700189 final CharSequence mText;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700190 final String mHtmlText;
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700191 final Intent mIntent;
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100192 Uri mUri;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700193
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700194 /**
195 * Create an Item consisting of a single block of (possibly styled) text.
196 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700197 public Item(CharSequence text) {
198 mText = text;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700199 mHtmlText = null;
200 mIntent = null;
201 mUri = null;
202 }
203
204 /**
205 * Create an Item consisting of a single block of (possibly styled) text,
206 * with an alternative HTML formatted representation. You <em>must</em>
207 * supply a plain text representation in addition to HTML text; coercion
208 * will not be done from HTML formated text into plain text.
209 */
210 public Item(CharSequence text, String htmlText) {
211 mText = text;
212 mHtmlText = htmlText;
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700213 mIntent = null;
214 mUri = null;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700215 }
216
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700217 /**
218 * Create an Item consisting of an arbitrary Intent.
219 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700220 public Item(Intent intent) {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700221 mText = null;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700222 mHtmlText = null;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700223 mIntent = intent;
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700224 mUri = null;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700225 }
226
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700227 /**
228 * Create an Item consisting of an arbitrary URI.
229 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700230 public Item(Uri uri) {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700231 mText = null;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700232 mHtmlText = null;
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700233 mIntent = null;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700234 mUri = uri;
235 }
236
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700237 /**
238 * Create a complex Item, containing multiple representations of
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700239 * text, Intent, and/or URI.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700240 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700241 public Item(CharSequence text, Intent intent, Uri uri) {
242 mText = text;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700243 mHtmlText = null;
244 mIntent = intent;
245 mUri = uri;
246 }
247
248 /**
249 * Create a complex Item, containing multiple representations of
250 * text, HTML text, Intent, and/or URI. If providing HTML text, you
251 * <em>must</em> supply a plain text representation as well; coercion
252 * will not be done from HTML formated text into plain text.
253 */
254 public Item(CharSequence text, String htmlText, Intent intent, Uri uri) {
255 if (htmlText != null && text == null) {
256 throw new IllegalArgumentException(
257 "Plain text must be supplied if HTML text is supplied");
258 }
259 mText = text;
260 mHtmlText = htmlText;
Dianne Hackborn9f531192010-08-04 17:48:03 -0700261 mIntent = intent;
262 mUri = uri;
263 }
264
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700265 /**
266 * Retrieve the raw text contained in this Item.
267 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700268 public CharSequence getText() {
269 return mText;
270 }
271
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700272 /**
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700273 * Retrieve the raw HTML text contained in this Item.
274 */
275 public String getHtmlText() {
276 return mHtmlText;
277 }
278
279 /**
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700280 * Retrieve the raw Intent contained in this Item.
281 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700282 public Intent getIntent() {
283 return mIntent;
284 }
285
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700286 /**
287 * Retrieve the raw URI contained in this Item.
288 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700289 public Uri getUri() {
290 return mUri;
291 }
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700292
293 /**
294 * Turn this item into text, regardless of the type of data it
295 * actually contains.
296 *
297 * <p>The algorithm for deciding what text to return is:
298 * <ul>
299 * <li> If {@link #getText} is non-null, return that.
300 * <li> If {@link #getUri} is non-null, try to retrieve its data
301 * as a text stream from its content provider. If this succeeds, copy
302 * the text into a String and return it. If it is not a content: URI or
303 * the content provider does not supply a text representation, return
304 * the raw URI as a string.
305 * <li> If {@link #getIntent} is non-null, convert that to an intent:
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700306 * URI and return it.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700307 * <li> Otherwise, return an empty string.
308 * </ul>
309 *
310 * @param context The caller's Context, from which its ContentResolver
311 * and other things can be retrieved.
312 * @return Returns the item's textual representation.
313 */
314//BEGIN_INCLUDE(coerceToText)
315 public CharSequence coerceToText(Context context) {
316 // If this Item has an explicit textual value, simply return that.
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700317 CharSequence text = getText();
318 if (text != null) {
319 return text;
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700320 }
321
322 // If this Item has a URI value, try using that.
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700323 Uri uri = getUri();
324 if (uri != null) {
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700325
326 // First see if the URI can be opened as a plain text stream
327 // (of any sub-type). If so, this is the best textual
328 // representation for it.
329 FileInputStream stream = null;
330 try {
331 // Ask for a stream of the desired type.
332 AssetFileDescriptor descr = context.getContentResolver()
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700333 .openTypedAssetFileDescriptor(uri, "text/*", null);
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700334 stream = descr.createInputStream();
335 InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
336
337 // Got it... copy the stream into a local string and return it.
338 StringBuilder builder = new StringBuilder(128);
339 char[] buffer = new char[8192];
340 int len;
341 while ((len=reader.read(buffer)) > 0) {
342 builder.append(buffer, 0, len);
343 }
344 return builder.toString();
345
346 } catch (FileNotFoundException e) {
347 // Unable to open content URI as text... not really an
348 // error, just something to ignore.
349
350 } catch (IOException e) {
351 // Something bad has happened.
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -0700352 Log.w("ClipData", "Failure loading text", e);
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700353 return e.toString();
354
355 } finally {
356 if (stream != null) {
357 try {
358 stream.close();
359 } catch (IOException e) {
360 }
361 }
362 }
363
364 // If we couldn't open the URI as a stream, then the URI itself
365 // probably serves fairly well as a textual representation.
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700366 return uri.toString();
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700367 }
368
369 // Finally, if all we have is an Intent, then we can just turn that
370 // into text. Not the most user-friendly thing, but it's something.
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700371 Intent intent = getIntent();
372 if (intent != null) {
373 return intent.toUri(Intent.URI_INTENT_SCHEME);
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700374 }
375
376 // Shouldn't get here, but just in case...
377 return "";
378 }
379//END_INCLUDE(coerceToText)
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800380
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700381 /**
382 * Like {@link #coerceToHtmlText(Context)}, but any text that would
383 * be returned as HTML formatting will be returned as text with
384 * style spans.
385 * @param context The caller's Context, from which its ContentResolver
386 * and other things can be retrieved.
387 * @return Returns the item's textual representation.
388 */
389 public CharSequence coerceToStyledText(Context context) {
390 CharSequence text = getText();
391 if (text instanceof Spanned) {
392 return text;
393 }
394 String htmlText = getHtmlText();
395 if (htmlText != null) {
396 try {
397 CharSequence newText = Html.fromHtml(htmlText);
398 if (newText != null) {
399 return newText;
400 }
401 } catch (RuntimeException e) {
402 // If anything bad happens, we'll fall back on the plain text.
403 }
404 }
405
406 if (text != null) {
407 return text;
408 }
409 return coerceToHtmlOrStyledText(context, true);
410 }
411
412 /**
413 * Turn this item into HTML text, regardless of the type of data it
414 * actually contains.
415 *
416 * <p>The algorithm for deciding what text to return is:
417 * <ul>
418 * <li> If {@link #getHtmlText} is non-null, return that.
419 * <li> If {@link #getText} is non-null, return that, converting to
420 * valid HTML text. If this text contains style spans,
421 * {@link Html#toHtml(Spanned) Html.toHtml(Spanned)} is used to
422 * convert them to HTML formatting.
423 * <li> If {@link #getUri} is non-null, try to retrieve its data
424 * as a text stream from its content provider. If the provider can
425 * supply text/html data, that will be preferred and returned as-is.
426 * Otherwise, any text/* data will be returned and escaped to HTML.
427 * If it is not a content: URI or the content provider does not supply
428 * a text representation, HTML text containing a link to the URI
429 * will be returned.
430 * <li> If {@link #getIntent} is non-null, convert that to an intent:
431 * URI and return as an HTML link.
432 * <li> Otherwise, return an empty string.
433 * </ul>
434 *
435 * @param context The caller's Context, from which its ContentResolver
436 * and other things can be retrieved.
437 * @return Returns the item's representation as HTML text.
438 */
439 public String coerceToHtmlText(Context context) {
440 // If the item has an explicit HTML value, simply return that.
441 String htmlText = getHtmlText();
442 if (htmlText != null) {
443 return htmlText;
444 }
445
446 // If this Item has a plain text value, return it as HTML.
447 CharSequence text = getText();
448 if (text != null) {
449 if (text instanceof Spanned) {
450 return Html.toHtml((Spanned)text);
451 }
452 return Html.escapeHtml(text);
453 }
454
455 text = coerceToHtmlOrStyledText(context, false);
456 return text != null ? text.toString() : null;
457 }
458
459 private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) {
460 // If this Item has a URI value, try using that.
461 if (mUri != null) {
462
463 // Check to see what data representations the content
464 // provider supports. We would like HTML text, but if that
465 // is not possible we'll live with plan text.
Vladislav Kaznacheevc14df8e2016-01-22 11:49:13 -0800466 String[] types = null;
467 try {
468 types = context.getContentResolver().getStreamTypes(mUri, "text/*");
469 } catch (SecurityException e) {
470 // No read permission for mUri, assume empty stream types list.
471 }
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700472 boolean hasHtml = false;
473 boolean hasText = false;
474 if (types != null) {
475 for (String type : types) {
476 if ("text/html".equals(type)) {
477 hasHtml = true;
478 } else if (type.startsWith("text/")) {
479 hasText = true;
480 }
481 }
482 }
483
484 // If the provider can serve data we can use, open and load it.
485 if (hasHtml || hasText) {
486 FileInputStream stream = null;
487 try {
488 // Ask for a stream of the desired type.
489 AssetFileDescriptor descr = context.getContentResolver()
490 .openTypedAssetFileDescriptor(mUri,
491 hasHtml ? "text/html" : "text/plain", null);
492 stream = descr.createInputStream();
493 InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
494
495 // Got it... copy the stream into a local string and return it.
496 StringBuilder builder = new StringBuilder(128);
497 char[] buffer = new char[8192];
498 int len;
499 while ((len=reader.read(buffer)) > 0) {
500 builder.append(buffer, 0, len);
501 }
502 String text = builder.toString();
503 if (hasHtml) {
504 if (styled) {
505 // We loaded HTML formatted text and the caller
506 // want styled text, convert it.
507 try {
508 CharSequence newText = Html.fromHtml(text);
509 return newText != null ? newText : text;
510 } catch (RuntimeException e) {
511 return text;
512 }
513 } else {
514 // We loaded HTML formatted text and that is what
515 // the caller wants, just return it.
516 return text.toString();
517 }
518 }
519 if (styled) {
520 // We loaded plain text and the caller wants styled
521 // text, that is all we have so return it.
522 return text;
523 } else {
524 // We loaded plain text and the caller wants HTML
525 // text, escape it for HTML.
526 return Html.escapeHtml(text);
527 }
528
529 } catch (FileNotFoundException e) {
530 // Unable to open content URI as text... not really an
531 // error, just something to ignore.
532
533 } catch (IOException e) {
534 // Something bad has happened.
Vladislav Kaznacheev7e4669c2016-05-02 17:04:08 -0700535 Log.w("ClipData", "Failure loading text", e);
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700536 return Html.escapeHtml(e.toString());
537
538 } finally {
539 if (stream != null) {
540 try {
541 stream.close();
542 } catch (IOException e) {
543 }
544 }
545 }
546 }
547
548 // If we couldn't open the URI as a stream, then we can build
549 // some HTML text with the URI itself.
550 // probably serves fairly well as a textual representation.
551 if (styled) {
552 return uriToStyledText(mUri.toString());
553 } else {
554 return uriToHtml(mUri.toString());
555 }
556 }
557
558 // Finally, if all we have is an Intent, then we can just turn that
559 // into text. Not the most user-friendly thing, but it's something.
560 if (mIntent != null) {
561 if (styled) {
562 return uriToStyledText(mIntent.toUri(Intent.URI_INTENT_SCHEME));
563 } else {
564 return uriToHtml(mIntent.toUri(Intent.URI_INTENT_SCHEME));
565 }
566 }
567
568 // Shouldn't get here, but just in case...
569 return "";
570 }
571
572 private String uriToHtml(String uri) {
573 StringBuilder builder = new StringBuilder(256);
574 builder.append("<a href=\"");
Nick Kralevichc92db392012-07-27 13:22:20 -0700575 builder.append(Html.escapeHtml(uri));
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700576 builder.append("\">");
577 builder.append(Html.escapeHtml(uri));
578 builder.append("</a>");
579 return builder.toString();
580 }
581
582 private CharSequence uriToStyledText(String uri) {
583 SpannableStringBuilder builder = new SpannableStringBuilder();
584 builder.append(uri);
585 builder.setSpan(new URLSpan(uri), 0, builder.length(),
586 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
587 return builder;
588 }
589
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800590 @Override
591 public String toString() {
592 StringBuilder b = new StringBuilder(128);
593
594 b.append("ClipData.Item { ");
595 toShortString(b);
596 b.append(" }");
597
598 return b.toString();
599 }
600
601 /** @hide */
602 public void toShortString(StringBuilder b) {
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700603 if (mHtmlText != null) {
604 b.append("H:");
605 b.append(mHtmlText);
606 } else if (mText != null) {
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800607 b.append("T:");
608 b.append(mText);
609 } else if (mUri != null) {
610 b.append("U:");
611 b.append(mUri);
612 } else if (mIntent != null) {
613 b.append("I:");
614 mIntent.toShortString(b, true, true, true, true);
615 } else {
616 b.append("NULL");
617 }
618 }
Dianne Hackbornae498722015-08-14 13:29:47 -0700619
620 /** @hide */
621 public void toShortSummaryString(StringBuilder b) {
622 if (mHtmlText != null) {
623 b.append("HTML");
624 } else if (mText != null) {
625 b.append("TEXT");
626 } else if (mUri != null) {
627 b.append("U:");
628 b.append(mUri);
629 } else if (mIntent != null) {
630 b.append("I:");
631 mIntent.toShortString(b, true, true, true, true);
632 } else {
633 b.append("NULL");
634 }
635 }
Dianne Hackborn9f531192010-08-04 17:48:03 -0700636 }
637
638 /**
639 * Create a new clip.
640 *
641 * @param label Label to show to the user describing this clip.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700642 * @param mimeTypes An array of MIME types this data is available as.
Dianne Hackborn9f531192010-08-04 17:48:03 -0700643 * @param item The contents of the first item in the clip.
644 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800645 public ClipData(CharSequence label, String[] mimeTypes, Item item) {
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700646 mClipDescription = new ClipDescription(label, mimeTypes);
Dianne Hackborn9f531192010-08-04 17:48:03 -0700647 if (item == null) {
648 throw new NullPointerException("item is null");
649 }
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800650 mIcon = null;
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800651 mItems = new ArrayList<Item>();
Dianne Hackborn9f531192010-08-04 17:48:03 -0700652 mItems.add(item);
653 }
654
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700655 /**
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700656 * Create a new clip.
657 *
658 * @param description The ClipDescription describing the clip contents.
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700659 * @param item The contents of the first item in the clip.
660 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800661 public ClipData(ClipDescription description, Item item) {
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700662 mClipDescription = description;
663 if (item == null) {
664 throw new NullPointerException("item is null");
665 }
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800666 mIcon = null;
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800667 mItems = new ArrayList<Item>();
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700668 mItems.add(item);
669 }
670
671 /**
Steve McKay57eaaf62016-06-13 12:53:31 -0700672 * Create a new clip.
673 *
674 * @param description The ClipDescription describing the clip contents.
675 * @param items The items in the clip. Not that a defensive copy of this
676 * list is not made, so caution should be taken to ensure the
677 * list is not available for further modification.
678 * @hide
679 */
680 public ClipData(ClipDescription description, ArrayList<Item> items) {
681 mClipDescription = description;
682 if (items == null) {
683 throw new NullPointerException("item is null");
684 }
685 mIcon = null;
686 mItems = items;
687 }
688
689 /**
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800690 * Create a new clip that is a copy of another clip. This does a deep-copy
691 * of all items in the clip.
692 *
693 * @param other The existing ClipData that is to be copied.
694 */
695 public ClipData(ClipData other) {
696 mClipDescription = other.mClipDescription;
697 mIcon = other.mIcon;
698 mItems = new ArrayList<Item>(other.mItems);
699 }
700
701 /**
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700702 * Create a new ClipData holding data of the type
703 * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700704 *
705 * @param label User-visible label for the clip data.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700706 * @param text The actual text in the clip.
707 * @return Returns a new ClipData containing the specified data.
708 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800709 static public ClipData newPlainText(CharSequence label, CharSequence text) {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700710 Item item = new Item(text);
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800711 return new ClipData(label, MIMETYPES_TEXT_PLAIN, item);
712 }
713
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700714 /**
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700715 * Create a new ClipData holding data of the type
716 * {@link ClipDescription#MIMETYPE_TEXT_HTML}.
717 *
718 * @param label User-visible label for the clip data.
719 * @param text The text of clip as plain text, for receivers that don't
720 * handle HTML. This is required.
721 * @param htmlText The actual HTML text in the clip.
722 * @return Returns a new ClipData containing the specified data.
723 */
724 static public ClipData newHtmlText(CharSequence label, CharSequence text,
725 String htmlText) {
726 Item item = new Item(text, htmlText);
727 return new ClipData(label, MIMETYPES_TEXT_HTML, item);
728 }
729
730 /**
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700731 * Create a new ClipData holding an Intent with MIME type
732 * {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700733 *
734 * @param label User-visible label for the clip data.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700735 * @param intent The actual Intent in the clip.
736 * @return Returns a new ClipData containing the specified data.
737 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800738 static public ClipData newIntent(CharSequence label, Intent intent) {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700739 Item item = new Item(intent);
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800740 return new ClipData(label, MIMETYPES_TEXT_INTENT, item);
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700741 }
742
743 /**
744 * Create a new ClipData holding a URI. If the URI is a content: URI,
745 * this will query the content provider for the MIME type of its data and
746 * use that as the MIME type. Otherwise, it will use the MIME type
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700747 * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700748 *
749 * @param resolver ContentResolver used to get information about the URI.
750 * @param label User-visible label for the clip data.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700751 * @param uri The URI in the clip.
752 * @return Returns a new ClipData containing the specified data.
753 */
754 static public ClipData newUri(ContentResolver resolver, CharSequence label,
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800755 Uri uri) {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700756 Item item = new Item(uri);
757 String[] mimeTypes = null;
758 if ("content".equals(uri.getScheme())) {
759 String realType = resolver.getType(uri);
760 mimeTypes = resolver.getStreamTypes(uri, "*/*");
Vladislav Kaznacheevf67661c2016-05-03 15:02:33 -0700761 if (realType != null) {
762 if (mimeTypes == null) {
763 mimeTypes = new String[] { realType };
764 } else {
765 String[] tmp = new String[mimeTypes.length + 1];
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700766 tmp[0] = realType;
Vladislav Kaznacheevf67661c2016-05-03 15:02:33 -0700767 System.arraycopy(mimeTypes, 0, tmp, 1, mimeTypes.length);
768 mimeTypes = tmp;
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700769 }
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700770 }
771 }
772 if (mimeTypes == null) {
773 mimeTypes = MIMETYPES_TEXT_URILIST;
774 }
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800775 return new ClipData(label, mimeTypes, item);
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700776 }
777
778 /**
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700779 * Create a new ClipData holding an URI with MIME type
780 * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800781 * Unlike {@link #newUri(ContentResolver, CharSequence, Uri)}, nothing
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700782 * is inferred about the URI -- if it is a content: URI holding a bitmap,
783 * the reported type will still be uri-list. Use this with care!
784 *
785 * @param label User-visible label for the clip data.
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700786 * @param uri The URI in the clip.
787 * @return Returns a new ClipData containing the specified data.
788 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800789 static public ClipData newRawUri(CharSequence label, Uri uri) {
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700790 Item item = new Item(uri);
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800791 return new ClipData(label, MIMETYPES_TEXT_URILIST, item);
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700792 }
793
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700794 /**
795 * Return the {@link ClipDescription} associated with this data, describing
796 * what it contains.
797 */
798 public ClipDescription getDescription() {
799 return mClipDescription;
800 }
Vladislav Kaznacheevf67661c2016-05-03 15:02:33 -0700801
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800802 /**
803 * Add a new Item to the overall ClipData container.
Vladislav Kaznacheevf67661c2016-05-03 15:02:33 -0700804 * <p> This method will <em>not</em> update the list of available MIME types in the
805 * {@link ClipDescription}. It should be used only when adding items which do not add new
806 * MIME types to this clip. If this is not the case, {@link #ClipData(CharSequence, String[],
807 * Item)} should be used with a complete list of MIME types.
808 * @param item Item to be added.
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800809 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700810 public void addItem(Item item) {
811 if (item == null) {
812 throw new NullPointerException("item is null");
813 }
814 mItems.add(item);
815 }
816
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800817 /** @hide */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700818 public Bitmap getIcon() {
819 return mIcon;
820 }
821
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800822 /**
823 * Return the number of items in the clip data.
824 */
Dianne Hackborn9f531192010-08-04 17:48:03 -0700825 public int getItemCount() {
826 return mItems.size();
827 }
828
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800829 /**
830 * Return a single item inside of the clip data. The index can range
831 * from 0 to {@link #getItemCount()}-1.
832 */
833 public Item getItemAt(int index) {
Dianne Hackborn9f531192010-08-04 17:48:03 -0700834 return mItems.get(index);
835 }
836
Jeff Sharkeya14acd22013-04-02 18:27:45 -0700837 /**
838 * Prepare this {@link ClipData} to leave an app process.
839 *
840 * @hide
841 */
Jeff Sharkey344744b2016-01-28 19:03:30 -0700842 public void prepareToLeaveProcess(boolean leavingPackage) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -0700843 final int size = mItems.size();
844 for (int i = 0; i < size; i++) {
845 final Item item = mItems.get(i);
846 if (item.mIntent != null) {
Jeff Sharkey344744b2016-01-28 19:03:30 -0700847 item.mIntent.prepareToLeaveProcess(leavingPackage);
Jeff Sharkeya14acd22013-04-02 18:27:45 -0700848 }
Jeff Sharkey344744b2016-01-28 19:03:30 -0700849 if (item.mUri != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -0700850 item.mUri.checkFileUriExposed("ClipData.Item.getUri()");
851 }
852 }
853 }
854
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700855 /** {@hide} */
856 public void prepareToEnterProcess() {
857 final int size = mItems.size();
858 for (int i = 0; i < size; i++) {
859 final Item item = mItems.get(i);
860 if (item.mIntent != null) {
861 item.mIntent.prepareToEnterProcess();
862 }
863 }
864 }
865
Nicolas Prevotd1c99b12014-07-04 16:56:17 +0100866 /** @hide */
867 public void fixUris(int contentUserHint) {
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100868 final int size = mItems.size();
869 for (int i = 0; i < size; i++) {
870 final Item item = mItems.get(i);
871 if (item.mIntent != null) {
Nicolas Prevotd1c99b12014-07-04 16:56:17 +0100872 item.mIntent.fixUris(contentUserHint);
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100873 }
874 if (item.mUri != null) {
Nicolas Prevotd1c99b12014-07-04 16:56:17 +0100875 item.mUri = maybeAddUserId(item.mUri, contentUserHint);
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100876 }
877 }
878 }
879
Nicolas Prevotf1939902014-06-25 09:29:02 +0100880 /**
881 * Only fixing the data field of the intents
882 * @hide
883 */
884 public void fixUrisLight(int contentUserHint) {
885 final int size = mItems.size();
886 for (int i = 0; i < size; i++) {
887 final Item item = mItems.get(i);
888 if (item.mIntent != null) {
889 Uri data = item.mIntent.getData();
890 if (data != null) {
891 item.mIntent.setData(maybeAddUserId(data, contentUserHint));
892 }
893 }
894 if (item.mUri != null) {
895 item.mUri = maybeAddUserId(item.mUri, contentUserHint);
896 }
897 }
898 }
899
Dianne Hackborn9f531192010-08-04 17:48:03 -0700900 @Override
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800901 public String toString() {
902 StringBuilder b = new StringBuilder(128);
903
904 b.append("ClipData { ");
905 toShortString(b);
906 b.append(" }");
907
908 return b.toString();
909 }
910
911 /** @hide */
912 public void toShortString(StringBuilder b) {
913 boolean first;
914 if (mClipDescription != null) {
915 first = !mClipDescription.toShortString(b);
916 } else {
917 first = true;
918 }
919 if (mIcon != null) {
920 if (!first) {
921 b.append(' ');
922 }
923 first = false;
924 b.append("I:");
925 b.append(mIcon.getWidth());
926 b.append('x');
927 b.append(mIcon.getHeight());
928 }
929 for (int i=0; i<mItems.size(); i++) {
930 if (!first) {
931 b.append(' ');
932 }
933 first = false;
934 b.append('{');
935 mItems.get(i).toShortString(b);
936 b.append('}');
937 }
938 }
939
Dianne Hackbornae498722015-08-14 13:29:47 -0700940 /** @hide */
941 public void toShortStringShortItems(StringBuilder b, boolean first) {
942 if (mItems.size() > 0) {
943 if (!first) {
944 b.append(' ');
945 }
946 mItems.get(0).toShortString(b);
947 if (mItems.size() > 1) {
948 b.append(" ...");
949 }
950 }
951 }
952
Vladislav Kaznacheev9149d2b2015-12-15 12:16:28 -0800953 /** @hide */
954 public void collectUris(List<Uri> out) {
955 for (int i = 0; i < mItems.size(); ++i) {
956 ClipData.Item item = getItemAt(i);
957
958 if (item.getUri() != null) {
959 out.add(item.getUri());
960 }
961
962 Intent intent = item.getIntent();
963 if (intent != null) {
964 if (intent.getData() != null) {
965 out.add(intent.getData());
966 }
967 if (intent.getClipData() != null) {
968 intent.getClipData().collectUris(out);
969 }
970 }
971 }
972 }
973
Dianne Hackborn21c241e2012-03-08 13:57:23 -0800974 @Override
Dianne Hackborn9f531192010-08-04 17:48:03 -0700975 public int describeContents() {
976 return 0;
977 }
978
979 @Override
980 public void writeToParcel(Parcel dest, int flags) {
Dianne Hackbornf834dfa2010-10-26 12:43:57 -0700981 mClipDescription.writeToParcel(dest, flags);
Dianne Hackborn9f531192010-08-04 17:48:03 -0700982 if (mIcon != null) {
983 dest.writeInt(1);
984 mIcon.writeToParcel(dest, flags);
985 } else {
986 dest.writeInt(0);
987 }
988 final int N = mItems.size();
989 dest.writeInt(N);
990 for (int i=0; i<N; i++) {
991 Item item = mItems.get(i);
992 TextUtils.writeToParcel(item.mText, dest, flags);
Dianne Hackbornacb69bb2012-04-13 15:36:06 -0700993 dest.writeString(item.mHtmlText);
Dianne Hackborn9f531192010-08-04 17:48:03 -0700994 if (item.mIntent != null) {
995 dest.writeInt(1);
996 item.mIntent.writeToParcel(dest, flags);
997 } else {
998 dest.writeInt(0);
999 }
1000 if (item.mUri != null) {
1001 dest.writeInt(1);
1002 item.mUri.writeToParcel(dest, flags);
1003 } else {
1004 dest.writeInt(0);
1005 }
1006 }
1007 }
1008
Dianne Hackborn1040dc42010-08-26 22:11:06 -07001009 ClipData(Parcel in) {
Dianne Hackbornf834dfa2010-10-26 12:43:57 -07001010 mClipDescription = new ClipDescription(in);
Dianne Hackborn9f531192010-08-04 17:48:03 -07001011 if (in.readInt() != 0) {
1012 mIcon = Bitmap.CREATOR.createFromParcel(in);
Dianne Hackborn1040dc42010-08-26 22:11:06 -07001013 } else {
1014 mIcon = null;
Dianne Hackborn9f531192010-08-04 17:48:03 -07001015 }
Dianne Hackborn21c241e2012-03-08 13:57:23 -08001016 mItems = new ArrayList<Item>();
Dianne Hackborn9f531192010-08-04 17:48:03 -07001017 final int N = in.readInt();
1018 for (int i=0; i<N; i++) {
1019 CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
Dianne Hackbornacb69bb2012-04-13 15:36:06 -07001020 String htmlText = in.readString();
Dianne Hackborn9f531192010-08-04 17:48:03 -07001021 Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
1022 Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
Dianne Hackbornacb69bb2012-04-13 15:36:06 -07001023 mItems.add(new Item(text, htmlText, intent, uri));
Dianne Hackborn9f531192010-08-04 17:48:03 -07001024 }
1025 }
1026
Dianne Hackborn1040dc42010-08-26 22:11:06 -07001027 public static final Parcelable.Creator<ClipData> CREATOR =
1028 new Parcelable.Creator<ClipData>() {
Dianne Hackborn9f531192010-08-04 17:48:03 -07001029
Steve McKay57eaaf62016-06-13 12:53:31 -07001030 @Override
Dianne Hackborn1040dc42010-08-26 22:11:06 -07001031 public ClipData createFromParcel(Parcel source) {
1032 return new ClipData(source);
Dianne Hackborn9f531192010-08-04 17:48:03 -07001033 }
1034
Steve McKay57eaaf62016-06-13 12:53:31 -07001035 @Override
Dianne Hackborn1040dc42010-08-26 22:11:06 -07001036 public ClipData[] newArray(int size) {
1037 return new ClipData[size];
Dianne Hackborn9f531192010-08-04 17:48:03 -07001038 }
1039 };
1040}