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