content: /**
   * Copyright (c) 2010, The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.content;
  
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.os.PersistableBundle;
  import android.text.TextUtils;
  import android.util.TimeUtils;
  import android.util.proto.ProtoOutputStream;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  
  /**
   * Meta-data describing the contents of a {@link ClipData}.  Provides enough
   * information to know if you can handle the ClipData, but not the data
   * itself.
   *
   * <div class="special reference">
   * <h3>Developer Guides</h3>
   * <p>For more information about using the clipboard framework, read the
   * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a>
   * developer guide.</p>
   * </div>
   */
  public class ClipDescription implements Parcelable {
      /**
       * The MIME type for a clip holding plain text.
       */
      public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
  
      /**
       * The MIME type for a clip holding HTML text.
       */
      public static final String MIMETYPE_TEXT_HTML = "text/html";
  
      /**
       * The MIME type for a clip holding one or more URIs.  This should be
       * used for URIs that are meaningful to a user (such as an http: URI).
       * It should <em>not</em> be used for a content: URI that references some
       * other piece of data; in that case the MIME type should be the type
       * of the referenced data.
       */
      public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
  
      /**
       * The MIME type for a clip holding an Intent.
       */
      public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
  
      /**
       * The name of the extra used to define a component name when copying/dragging
       * an app icon from Launcher.
       * <p>
       * Type: String
       * </p>
       * <p>
       * Use {@link ComponentName#unflattenFromString(String)}
       * and {@link ComponentName#flattenToString()} to convert the extra value
       * to/from {@link ComponentName}.
       * </p>
       * @hide
       */
      public static final String EXTRA_TARGET_COMPONENT_NAME =
              "android.content.extra.TARGET_COMPONENT_NAME";
  
      /**
       * The name of the extra used to define a user serial number when copying/dragging
       * an app icon from Launcher.
       * <p>
       * Type: long
       * </p>
       * @hide
       */
      public static final String EXTRA_USER_SERIAL_NUMBER =
              "android.content.extra.USER_SERIAL_NUMBER";
  
  
      final CharSequence mLabel;
      private final ArrayList<String> mMimeTypes;
      private PersistableBundle mExtras;
      private long mTimeStamp;
  
      /**
       * Create a new clip.
       *
       * @param label Label to show to the user describing this clip.
       * @param mimeTypes An array of MIME types this data is available as.
       */
      public ClipDescription(CharSequence label, String[] mimeTypes) {
          if (mimeTypes == null) {
              throw new NullPointerException("mimeTypes is null");
          }
          mLabel = label;
          mMimeTypes = new ArrayList<String>(Arrays.asList(mimeTypes));
      }
  
      /**
       * Create a copy of a ClipDescription.
       */
      public ClipDescription(ClipDescription o) {
          mLabel = o.mLabel;
          mMimeTypes = new ArrayList<String>(o.mMimeTypes);
          mTimeStamp = o.mTimeStamp;
      }
  
      /**
       * Helper to compare two MIME types, where one may be a pattern.
       * @param concreteType A fully-specified MIME type.
       * @param desiredType A desired MIME type that may be a pattern such as *&#47;*.
       * @return Returns true if the two MIME types match.
       */
      public static boolean compareMimeTypes(String concreteType, String desiredType) {
          final int typeLength = desiredType.length();
          if (typeLength == 3 && desiredType.equals("*/*")) {
              return true;
          }
  
          final int slashpos = desiredType.indexOf('/');
          if (slashpos > 0) {
              if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
                  if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
                      return true;
                  }
              } else if (desiredType.equals(concreteType)) {
                  return true;
              }
          }
  
          return false;
      }
  
      /**
       * Used for setting the timestamp at which the associated {@link ClipData} is copied to
       * global clipboard.
       *
       * @param timeStamp at which the associated {@link ClipData} is copied to clipboard in
       *                  {@link System#currentTimeMillis()} time base.
       * @hide
       */
      public void setTimestamp(long timeStamp) {
          mTimeStamp = timeStamp;
      }
  
      /**
       * Return the timestamp at which the associated {@link ClipData} is copied to global clipboard
       * in the {@link System#currentTimeMillis()} time base.
       *
       * @return timestamp at which the associated {@link ClipData} is copied to global clipboard
       *         or {@code 0} if it is not copied to clipboard.
       */
      public long getTimestamp() {
          return mTimeStamp;
      }
  
      /**
       * Return the label for this clip.
       */
      public CharSequence getLabel() {
          return mLabel;
      }
  
      /**
       * Check whether the clip description contains the given MIME type.
       *
       * @param mimeType The desired MIME type.  May be a pattern.
       * @return Returns true if one of the MIME types in the clip description
       * matches the desired MIME type, else false.
       */
      public boolean hasMimeType(String mimeType) {
          final int size = mMimeTypes.size();
          for (int i=0; i<size; i++) {
              if (compareMimeTypes(mMimeTypes.get(i), mimeType)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Filter the clip description MIME types by the given MIME type.  Returns
       * all MIME types in the clip that match the given MIME type.
       *
       * @param mimeType The desired MIME type.  May be a pattern.
       * @return Returns an array of all matching MIME types.  If there are no
       * matching MIME types, null is returned.
       */
      public String[] filterMimeTypes(String mimeType) {
          ArrayList<String> array = null;
          final int size = mMimeTypes.size();
          for (int i=0; i<size; i++) {
              if (compareMimeTypes(mMimeTypes.get(i), mimeType)) {
                  if (array == null) {
                      array = new ArrayList<String>();
                  }
                  array.add(mMimeTypes.get(i));
              }
          }
          if (array == null) {
              return null;
          }
          String[] rawArray = new String[array.size()];
          array.toArray(rawArray);
          return rawArray;
      }
  
      /**
       * Return the number of MIME types the clip is available in.
       */
      public int getMimeTypeCount() {
          return mMimeTypes.size();
      }
  
      /**
       * Return one of the possible clip MIME types.
       */
      public String getMimeType(int index) {
          return mMimeTypes.get(index);
      }
  
      /**
       * Add MIME types to the clip description.
       */
      void addMimeTypes(String[] mimeTypes) {
          for (int i=0; i!=mimeTypes.length; i++) {
              final String mimeType = mimeTypes[i];
              if (!mMimeTypes.contains(mimeType)) {
                  mMimeTypes.add(mimeType);
              }
          }
      }
  
      /**
       * Retrieve extended data from the clip description.
       *
       * @return the bundle containing extended data previously set with
       * {@link #setExtras(PersistableBundle)}, or null if no extras have been set.
       *
       * @see #setExtras(PersistableBundle)
       */
      public PersistableBundle getExtras() {
          return mExtras;
      }
  
      /**
       * Add extended data to the clip description.
       *
       * @see #getExtras()
       */
      public void setExtras(PersistableBundle extras) {
          mExtras = new PersistableBundle(extras);
      }
  
      /** @hide */
      public void validate() {
          if (mMimeTypes == null) {
              throw new NullPointerException("null mime types");
          }
          final int size = mMimeTypes.size();
          if (size <= 0) {
              throw new IllegalArgumentException("must have at least 1 mime type");
          }
          for (int i=0; i<size; i++) {
              if (mMimeTypes.get(i) == null) {
                  throw new NullPointerException("mime type at " + i + " is null");
              }
          }
      }
  
      @Override
      public String toString() {
          StringBuilder b = new StringBuilder(128);
  
          b.append("ClipDescription { ");
          toShortString(b);
          b.append(" }");
  
          return b.toString();
      }
  
      /** @hide */
      public boolean toShortString(StringBuilder b) {
          boolean first = !toShortStringTypesOnly(b);
          if (mLabel != null) {
              if (!first) {
                  b.append(' ');
              }
              first = false;
              b.append('"');
              b.append(mLabel);
              b.append('"');
          }
          if (mExtras != null) {
              if (!first) {
                  b.append(' ');
              }
              first = false;
              b.append(mExtras.toString());
          }
          if (mTimeStamp > 0) {
              if (!first) {
                  b.append(' ');
              }
              first = false;
              b.append('<');
              b.append(TimeUtils.logTimeOfDay(mTimeStamp));
              b.append('>');
          }
          return !first;
      }
  
      /** @hide */
      public boolean toShortStringTypesOnly(StringBuilder b) {
          boolean first = true;
          final int size = mMimeTypes.size();
          for (int i=0; i<size; i++) {
              if (!first) {
                  b.append(' ');
              }
              first = false;
              b.append(mMimeTypes.get(i));
          }
          return !first;
      }
  
      /** @hide */
      public void writeToProto(ProtoOutputStream proto, long fieldId) {
          final long token = proto.start(fieldId);
  
          final int size = mMimeTypes.size();
          for (int i = 0; i < size; i++) {
              proto.write(ClipDescriptionProto.MIME_TYPES, mMimeTypes.get(i));
          }
  
          if (mLabel != null) {
              proto.write(ClipDescriptionProto.LABEL, mLabel.toString());
          }
          if (mExtras != null) {
              mExtras.writeToProto(proto, ClipDescriptionProto.EXTRAS);
          }
          if (mTimeStamp > 0) {
              proto.write(ClipDescriptionProto.TIMESTAMP_MS, mTimeStamp);
          }
  
          proto.end(token);
      }
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          TextUtils.writeToParcel(mLabel, dest, flags);
          dest.writeStringList(mMimeTypes);
          dest.writePersistableBundle(mExtras);
          dest.writeLong(mTimeStamp);
      }
  
      ClipDescription(Parcel in) {
          mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
          mMimeTypes = in.createStringArrayList();
          mExtras = in.readPersistableBundle();
          mTimeStamp = in.readLong();
      }
  
      public static final @android.annotation.NonNull Parcelable.Creator<ClipDescription> CREATOR =
          new Parcelable.Creator<ClipDescription>() {
  
              public ClipDescription createFromParcel(Parcel source) {
                  return new ClipDescription(source);
              }
  
              public ClipDescription[] newArray(int size) {
                  return new ClipDescription[size];
              }
          };
  }
  