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
17package android.content;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.os.PersistableBundle;
22import android.text.TextUtils;
23
24import 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 */
38public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
195    public int getMimeTypeCount() {
196        return mMimeTypes.length;
197    }
198
199    /**
200     * Return one of the possible clip MIME types.
201     */
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     */
214    public PersistableBundle getExtras() {
215        return mExtras;
216    }
217
218    /**
219     * Add extended data to the clip description.
220     *
221     * @see #getExtras()
222     */
223    public void setExtras(PersistableBundle extras) {
224        mExtras = new PersistableBundle(extras);
225    }
226
227    /** @hide */
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
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 */
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 */
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
289    public int describeContents() {
290        return 0;
291    }
292
293    @Override
294    public void writeToParcel(Parcel dest, int flags) {
295        TextUtils.writeToParcel(mLabel, dest, flags);
296        dest.writeStringArray(mMimeTypes);
297        dest.writePersistableBundle(mExtras);
298    }
299
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