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