1 /**
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 
17 package android.content;
18 
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 import android.os.PersistableBundle;
22 import android.text.TextUtils;
23 
24 import java.util.ArrayList;
25 
26 /**
27  * Meta-data describing the contents of a {@link ClipData}.  Provides enough
28  * information to know if you can handle the ClipData, but not the data
29  * itself.
30  *
31  * <div class="special reference">
32  * <h3>Developer Guides</h3>
33  * <p>For more information about using the clipboard framework, read the
34  * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a>
35  * developer guide.</p>
36  * </div>
37  */
38 public class ClipDescription implements Parcelable {
39     /**
40      * The MIME type for a clip holding plain text.
41      */
42     public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
43 
44     /**
45      * The MIME type for a clip holding HTML text.
46      */
47     public static final String MIMETYPE_TEXT_HTML = "text/html";
48 
49     /**
50      * The MIME type for a clip holding one or more URIs.  This should be
51      * used for URIs that are meaningful to a user (such as an http: URI).
52      * It should <em>not</em> be used for a content: URI that references some
53      * other piece of data; in that case the MIME type should be the type
54      * of the referenced data.
55      */
56     public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
57 
58     /**
59      * The MIME type for a clip holding an Intent.
60      */
61     public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
62 
63     /**
64      * The name of the extra used to define a component name when copying/dragging
65      * an app icon from Launcher.
66      * <p>
67      * Type: String
68      * </p>
69      * <p>
70      * Use {@link ComponentName#unflattenFromString(String)}
71      * and {@link ComponentName#flattenToString()} to convert the extra value
72      * to/from {@link ComponentName}.
73      * </p>
74      * @hide
75      */
76     public static final String EXTRA_TARGET_COMPONENT_NAME =
77             "android.content.extra.TARGET_COMPONENT_NAME";
78 
79     /**
80      * The name of the extra used to define a user serial number when copying/dragging
81      * an app icon from Launcher.
82      * <p>
83      * Type: long
84      * </p>
85      * @hide
86      */
87     public static final String EXTRA_USER_SERIAL_NUMBER =
88             "android.content.extra.USER_SERIAL_NUMBER";
89 
90 
91     final CharSequence mLabel;
92     final String[] mMimeTypes;
93     private PersistableBundle mExtras;
94 
95     /**
96      * Create a new clip.
97      *
98      * @param label Label to show to the user describing this clip.
99      * @param mimeTypes An array of MIME types this data is available as.
100      */
ClipDescription(CharSequence label, String[] mimeTypes)101     public ClipDescription(CharSequence label, String[] mimeTypes) {
102         if (mimeTypes == null) {
103             throw new NullPointerException("mimeTypes is null");
104         }
105         mLabel = label;
106         mMimeTypes = mimeTypes;
107     }
108 
109     /**
110      * Create a copy of a ClipDescription.
111      */
ClipDescription(ClipDescription o)112     public ClipDescription(ClipDescription o) {
113         mLabel = o.mLabel;
114         mMimeTypes = o.mMimeTypes;
115     }
116 
117     /**
118      * Helper to compare two MIME types, where one may be a pattern.
119      * @param concreteType A fully-specified MIME type.
120      * @param desiredType A desired MIME type that may be a pattern such as *&#47;*.
121      * @return Returns true if the two MIME types match.
122      */
compareMimeTypes(String concreteType, String desiredType)123     public static boolean compareMimeTypes(String concreteType, String desiredType) {
124         final int typeLength = desiredType.length();
125         if (typeLength == 3 && desiredType.equals("*/*")) {
126             return true;
127         }
128 
129         final int slashpos = desiredType.indexOf('/');
130         if (slashpos > 0) {
131             if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
132                 if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
133                     return true;
134                 }
135             } else if (desiredType.equals(concreteType)) {
136                 return true;
137             }
138         }
139 
140         return false;
141     }
142 
143     /**
144      * Return the label for this clip.
145      */
getLabel()146     public CharSequence getLabel() {
147         return mLabel;
148     }
149 
150     /**
151      * Check whether the clip description contains the given MIME type.
152      *
153      * @param mimeType The desired MIME type.  May be a pattern.
154      * @return Returns true if one of the MIME types in the clip description
155      * matches the desired MIME type, else false.
156      */
hasMimeType(String mimeType)157     public boolean hasMimeType(String mimeType) {
158         for (int i=0; i<mMimeTypes.length; i++) {
159             if (compareMimeTypes(mMimeTypes[i], mimeType)) {
160                 return true;
161             }
162         }
163         return false;
164     }
165 
166     /**
167      * Filter the clip description MIME types by the given MIME type.  Returns
168      * all MIME types in the clip that match the given MIME type.
169      *
170      * @param mimeType The desired MIME type.  May be a pattern.
171      * @return Returns an array of all matching MIME types.  If there are no
172      * matching MIME types, null is returned.
173      */
filterMimeTypes(String mimeType)174     public String[] filterMimeTypes(String mimeType) {
175         ArrayList<String> array = null;
176         for (int i=0; i<mMimeTypes.length; i++) {
177             if (compareMimeTypes(mMimeTypes[i], mimeType)) {
178                 if (array == null) {
179                     array = new ArrayList<String>();
180                 }
181                 array.add(mMimeTypes[i]);
182             }
183         }
184         if (array == null) {
185             return null;
186         }
187         String[] rawArray = new String[array.size()];
188         array.toArray(rawArray);
189         return rawArray;
190     }
191 
192     /**
193      * Return the number of MIME types the clip is available in.
194      */
getMimeTypeCount()195     public int getMimeTypeCount() {
196         return mMimeTypes.length;
197     }
198 
199     /**
200      * Return one of the possible clip MIME types.
201      */
getMimeType(int index)202     public String getMimeType(int index) {
203         return mMimeTypes[index];
204     }
205 
206     /**
207      * Retrieve extended data from the clip description.
208      *
209      * @return the bundle containing extended data previously set with
210      * {@link #setExtras(PersistableBundle)}, or null if no extras have been set.
211      *
212      * @see #setExtras(PersistableBundle)
213      */
getExtras()214     public PersistableBundle getExtras() {
215         return mExtras;
216     }
217 
218     /**
219      * Add extended data to the clip description.
220      *
221      * @see #getExtras()
222      */
setExtras(PersistableBundle extras)223     public void setExtras(PersistableBundle extras) {
224         mExtras = new PersistableBundle(extras);
225     }
226 
227     /** @hide */
validate()228     public void validate() {
229         if (mMimeTypes == null) {
230             throw new NullPointerException("null mime types");
231         }
232         if (mMimeTypes.length <= 0) {
233             throw new IllegalArgumentException("must have at least 1 mime type");
234         }
235         for (int i=0; i<mMimeTypes.length; i++) {
236             if (mMimeTypes[i] == null) {
237                 throw new NullPointerException("mime type at " + i + " is null");
238             }
239         }
240     }
241 
242     @Override
toString()243     public String toString() {
244         StringBuilder b = new StringBuilder(128);
245 
246         b.append("ClipDescription { ");
247         toShortString(b);
248         b.append(" }");
249 
250         return b.toString();
251     }
252 
253     /** @hide */
toShortString(StringBuilder b)254     public boolean toShortString(StringBuilder b) {
255         boolean first = !toShortStringTypesOnly(b);
256         if (mLabel != null) {
257             if (!first) {
258                 b.append(' ');
259             }
260             first = false;
261             b.append('"');
262             b.append(mLabel);
263             b.append('"');
264         }
265         if (mExtras != null) {
266             if (!first) {
267                 b.append(' ');
268             }
269             first = false;
270             b.append(mExtras.toString());
271         }
272         return !first;
273     }
274 
275     /** @hide */
toShortStringTypesOnly(StringBuilder b)276     public boolean toShortStringTypesOnly(StringBuilder b) {
277         boolean first = true;
278         for (int i=0; i<mMimeTypes.length; i++) {
279             if (!first) {
280                 b.append(' ');
281             }
282             first = false;
283             b.append(mMimeTypes[i]);
284         }
285         return !first;
286     }
287 
288     @Override
describeContents()289     public int describeContents() {
290         return 0;
291     }
292 
293     @Override
writeToParcel(Parcel dest, int flags)294     public void writeToParcel(Parcel dest, int flags) {
295         TextUtils.writeToParcel(mLabel, dest, flags);
296         dest.writeStringArray(mMimeTypes);
297         dest.writePersistableBundle(mExtras);
298     }
299 
ClipDescription(Parcel in)300     ClipDescription(Parcel in) {
301         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
302         mMimeTypes = in.createStringArray();
303         mExtras = in.readPersistableBundle();
304     }
305 
306     public static final Parcelable.Creator<ClipDescription> CREATOR =
307         new Parcelable.Creator<ClipDescription>() {
308 
309             public ClipDescription createFromParcel(Parcel source) {
310                 return new ClipDescription(source);
311             }
312 
313             public ClipDescription[] newArray(int size) {
314                 return new ClipDescription[size];
315             }
316         };
317 }
318