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