1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
|
/**
* Copyright (c) 2010, 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.content;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
/**
* Representation of a clipped data on the clipboard.
*
* <p>ClippedData is a complex type containing one or Item instances,
* each of which can hold one or more representations of an item of data.
* For display to the user, it also has a label and iconic representation.</p>
*
* <p>Each Item instance can be one of three main classes of data: a simple
* CharSequence of text, a single Intent object, or a Uri. See {@link Item}
* for more details.
*
* <a name="ImplementingPaste"></a>
* <h3>Implementing Paste or Drop</h3>
*
* <p>To implement a paste or drop of a ClippedData object into an application,
* the application must correctly interpret the data for its use. If the {@link Item}
* it contains is simple text or an Intent, there is little to be done: text
* can only be interpreted as text, and an Intent will typically be used for
* creating shortcuts (such as placing icons on the home screen) or other
* actions.
*
* <p>If all you want is the textual representation of the clipped data, you
* can use the convenience method {@link Item#coerceToText Item.coerceToText}.
*
* <p>More complicated exchanges will be done through URIs, in particular
* "content:" URIs. A content URI allows the recipient of a ClippedData item
* to interact closely with the ContentProvider holding the data in order to
* negotiate the transfer of that data.
*
* <p>For example, here is the paste function of a simple NotePad application.
* When retrieving the data from the clipboard, it can do either two things:
* if the clipboard contains a URI reference to an existing note, it copies
* the entire structure of the note into a new note; otherwise, it simply
* coerces the clip into text and uses that as the new note's contents.
*
* {@sample development/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
* paste}
*
* <p>In many cases an application can paste various types of streams of data. For
* example, an e-mail application may want to allow the user to paste an image
* or other binary data as an attachment. This is accomplished through the
* ContentResolver {@link ContentResolver#getStreamTypes(Uri, String)} and
* {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, android.os.Bundle)}
* methods. These allow a client to discover the type(s) of data that a particular
* content URI can make available as a stream and retrieve the stream of data.
*
* <p>For example, the implementation of {@link Item#coerceToText Item.coerceToText}
* itself uses this to try to retrieve a URI clip as a stream of text:
*
* {@sample frameworks/base/core/java/android/content/ClippedData.java coerceToText}
*
* <a name="ImplementingCopy"></a>
* <h3>Implementing Copy or Drag</h3>
*
* <p>To be the source of a clip, the application must construct a ClippedData
* object that any recipient can interpret best for their context. If the clip
* is to contain a simple text, Intent, or URI, this is easy: an {@link Item}
* containing the appropriate data type can be constructed and used.
*
* <p>More complicated data types require the implementation of support in
* a ContentProvider for describing and generating the data for the recipient.
* A common scenario is one where an application places on the clipboard the
* content: URI of an object that the user has copied, with the data at that
* URI consisting of a complicated structure that only other applications with
* direct knowledge of the structure can use.
*
* <p>For applications that do not have intrinsic knowledge of the data structure,
* the content provider holding it can make the data available as an arbitrary
* number of types of data streams. This is done by implementing the
* ContentProvider {@link ContentProvider#getStreamTypes(Uri, String)} and
* {@link ContentProvider#openTypedAssetFile(Uri, String, android.os.Bundle)}
* methods.
*
* <p>Going back to our simple NotePad application, this is the implementation
* it may have to convert a single note URI (consisting of a title and the note
* text) into a stream of plain text data.
*
* {@sample development/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
* stream}
*
* <p>The copy operation in our NotePad application is now just a simple matter
* of making a clip containing the URI of the note being copied:
*
* {@sample development/samples/NotePad/src/com/example/android/notepad/NotesList.java
* copy}
*
* <p>Note if a paste operation needs this clip as text (for example to paste
* into an editor), then {@link Item#coerceToText(Context)} will ask the content
* provider for the clip URI as text and successfully paste the entire note.
*/
public class ClippedData implements Parcelable {
CharSequence mLabel;
Bitmap mIcon;
final ArrayList<Item> mItems = new ArrayList<Item>();
/**
* Description of a single item in a ClippedData.
*
* <p>The types than an individual item can currently contain are:</p>
*
* <ul>
* <li> Text: a basic string of text. This is actually a CharSequence,
* so it can be formatted text supported by corresponding Android built-in
* style spans. (Custom application spans are not supported and will be
* stripped when transporting through the clipboard.)
* <li> Intent: an arbitrary Intent object. A typical use is the shortcut
* to create when pasting a clipped item on to the home screen.
* <li> Uri: a URI reference. This may be any URI (such as an http: URI
* representing a bookmark), however it is often a content: URI. Using
* content provider references as clips like this allows an application to
* share complex or large clips through the standard content provider
* facilities.
* </ul>
*/
public static class Item {
CharSequence mText;
Intent mIntent;
Uri mUri;
/**
* Create an Item consisting of a single block of (possibly styled) text.
*/
public Item(CharSequence text) {
mText = text;
}
/**
* Create an Item consisting of an arbitrary Intent.
*/
public Item(Intent intent) {
mIntent = intent;
}
/**
* Create an Item consisting of an arbitrary URI.
*/
public Item(Uri uri) {
mUri = uri;
}
/**
* Create a complex Item, containing multiple representations of
* text, intent, and/or URI.
*/
public Item(CharSequence text, Intent intent, Uri uri) {
mText = text;
mIntent = intent;
mUri = uri;
}
/**
* Retrieve the raw text contained in this Item.
*/
public CharSequence getText() {
return mText;
}
/**
* Retrieve the raw Intent contained in this Item.
*/
public Intent getIntent() {
return mIntent;
}
/**
* Retrieve the raw URI contained in this Item.
*/
public Uri getUri() {
return mUri;
}
/**
* Turn this item into text, regardless of the type of data it
* actually contains.
*
* <p>The algorithm for deciding what text to return is:
* <ul>
* <li> If {@link #getText} is non-null, return that.
* <li> If {@link #getUri} is non-null, try to retrieve its data
* as a text stream from its content provider. If this succeeds, copy
* the text into a String and return it. If it is not a content: URI or
* the content provider does not supply a text representation, return
* the raw URI as a string.
* <li> If {@link #getIntent} is non-null, convert that to an intent:
* URI and returnit.
* <li> Otherwise, return an empty string.
* </ul>
*
* @param context The caller's Context, from which its ContentResolver
* and other things can be retrieved.
* @return Returns the item's textual representation.
*/
//BEGIN_INCLUDE(coerceToText)
public CharSequence coerceToText(Context context) {
// If this Item has an explicit textual value, simply return that.
if (mText != null) {
return mText;
}
// If this Item has a URI value, try using that.
if (mUri != null) {
// First see if the URI can be opened as a plain text stream
// (of any sub-type). If so, this is the best textual
// representation for it.
FileInputStream stream = null;
try {
// Ask for a stream of the desired type.
AssetFileDescriptor descr = context.getContentResolver()
.openTypedAssetFileDescriptor(mUri, "text/*", null);
stream = descr.createInputStream();
InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
// Got it... copy the stream into a local string and return it.
StringBuilder builder = new StringBuilder(128);
char[] buffer = new char[8192];
int len;
while ((len=reader.read(buffer)) > 0) {
builder.append(buffer, 0, len);
}
return builder.toString();
} catch (FileNotFoundException e) {
// Unable to open content URI as text... not really an
// error, just something to ignore.
} catch (IOException e) {
// Something bad has happened.
Log.w("ClippedData", "Failure loading text", e);
return e.toString();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
// If we couldn't open the URI as a stream, then the URI itself
// probably serves fairly well as a textual representation.
return mUri.toString();
}
// Finally, if all we have is an Intent, then we can just turn that
// into text. Not the most user-friendly thing, but it's something.
if (mIntent != null) {
return mIntent.toUri(Intent.URI_INTENT_SCHEME);
}
// Shouldn't get here, but just in case...
return "";
}
//END_INCLUDE(coerceToText)
}
/**
* Create a new clip.
*
* @param label Label to show to the user describing this clip.
* @param icon Bitmap providing the user with an iconing representation of
* the clip.
* @param item The contents of the first item in the clip.
*/
public ClippedData(CharSequence label, Bitmap icon, Item item) {
if (item == null) {
throw new NullPointerException("item is null");
}
mLabel = label;
mIcon = icon;
mItems.add(item);
}
public void addItem(Item item) {
if (item == null) {
throw new NullPointerException("item is null");
}
mItems.add(item);
}
public CharSequence getLabel() {
return mLabel;
}
public Bitmap getIcon() {
return mIcon;
}
public int getItemCount() {
return mItems.size();
}
public Item getItem(int index) {
return mItems.get(index);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
TextUtils.writeToParcel(mLabel, dest, flags);
if (mIcon != null) {
dest.writeInt(1);
mIcon.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
final int N = mItems.size();
dest.writeInt(N);
for (int i=0; i<N; i++) {
Item item = mItems.get(i);
TextUtils.writeToParcel(item.mText, dest, flags);
if (item.mIntent != null) {
dest.writeInt(1);
item.mIntent.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
if (item.mUri != null) {
dest.writeInt(1);
item.mUri.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
}
}
ClippedData(Parcel in) {
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readInt() != 0) {
mIcon = Bitmap.CREATOR.createFromParcel(in);
}
final int N = in.readInt();
for (int i=0; i<N; i++) {
CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
mItems.add(new Item(text, intent, uri));
}
}
public static final Parcelable.Creator<ClippedData> CREATOR =
new Parcelable.Creator<ClippedData>() {
public ClippedData createFromParcel(Parcel source) {
return new ClippedData(source);
}
public ClippedData[] newArray(int size) {
return new ClippedData[size];
}
};
}
|