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.mtp;
  
  import android.annotation.NonNull;
  import android.os.Build;
  
  import com.android.internal.util.Preconditions;
  
  import dalvik.system.VMRuntime;
  
  /**
   * This class encapsulates information about an object on an MTP device.
   * This corresponds to the ObjectInfo Dataset described in
   * section 5.3.1 of the MTP specification.
   */
  public final class MtpObjectInfo {
      private int mHandle;
      private int mStorageId;
      private int mFormat;
      private int mProtectionStatus;
      private int mCompressedSize;
      private int mThumbFormat;
      private int mThumbCompressedSize;
      private int mThumbPixWidth;
      private int mThumbPixHeight;
      private int mImagePixWidth;
      private int mImagePixHeight;
      private int mImagePixDepth;
      private int mParent;
      private int mAssociationType;
      private int mAssociationDesc;
      private int mSequenceNumber;
      private String mName = "";
      private long mDateCreated;
      private long mDateModified;
      private String mKeywords = "";
  
      // only instantiated via JNI or via a builder
      private MtpObjectInfo() {
      }
  
      /**
       * Returns the object handle for the MTP object
       *
       * @return the object handle
       */
      public final int getObjectHandle() {
          return mHandle;
      }
  
      /**
       * Returns the storage ID for the MTP object's storage unit
       *
       * @return the storage ID
       */
      public final int getStorageId() {
          return mStorageId;
      }
  
      /**
       * Returns the format code for the MTP object
       *
       * @return the format code
       */
      public final int getFormat() {
          return mFormat;
      }
  
      /**
       * Returns the protection status for the MTP object
       * Possible values are:
       *
       * <ul>
       * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NONE}
       * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_READ_ONLY}
       * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NON_TRANSFERABLE_DATA}
       * </ul>
       *
       * @return the protection status
       */
      public final int getProtectionStatus() {
          return mProtectionStatus;
      }
  
      /**
       * Returns the size of the MTP object
       *
       * @return the object size
       */
      public final int getCompressedSize() {
          Preconditions.checkState(mCompressedSize >= 0);
          return mCompressedSize;
      }
  
      /**
       * Returns the size of the MTP object
       *
       * @return the object size
       */
      public final long getCompressedSizeLong() {
          return uint32ToLong(mCompressedSize);
      }
  
      /**
       * Returns the format code for the MTP object's thumbnail
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail format code
       */
      public final int getThumbFormat() {
          return mThumbFormat;
      }
  
      /**
       * Returns the size of the MTP object's thumbnail
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail size
       */
      public final int getThumbCompressedSize() {
          Preconditions.checkState(mThumbCompressedSize >= 0);
          return mThumbCompressedSize;
      }
  
      /**
       * Returns the size of the MTP object's thumbnail
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail size
       */
      public final long getThumbCompressedSizeLong() {
          return uint32ToLong(mThumbCompressedSize);
      }
  
      /**
       * Returns the width of the MTP object's thumbnail in pixels
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail width
       */
      public final int getThumbPixWidth() {
          Preconditions.checkState(mThumbPixWidth >= 0);
          return mThumbPixWidth;
      }
  
      /**
       * Returns the width of the MTP object's thumbnail in pixels
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail width
       */
      public final long getThumbPixWidthLong() {
          return uint32ToLong(mThumbPixWidth);
      }
  
      /**
       * Returns the height of the MTP object's thumbnail in pixels
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail height
       */
      public final int getThumbPixHeight() {
          Preconditions.checkState(mThumbPixHeight >= 0);
          return mThumbPixHeight;
      }
  
      /**
       * Returns the height of the MTP object's thumbnail in pixels
       * Will be zero for objects with no thumbnail
       *
       * @return the thumbnail height
       */
      public final long getThumbPixHeightLong() {
          return uint32ToLong(mThumbPixHeight);
      }
  
      /**
       * Returns the width of the MTP object in pixels
       * Will be zero for non-image objects
       *
       * @return the image width
       */
      public final int getImagePixWidth() {
          Preconditions.checkState(mImagePixWidth >= 0);
          return mImagePixWidth;
      }
  
      /**
       * Returns the width of the MTP object in pixels
       * Will be zero for non-image objects
       *
       * @return the image width
       */
      public final long getImagePixWidthLong() {
          return uint32ToLong(mImagePixWidth);
      }
  
      /**
       * Returns the height of the MTP object in pixels
       * Will be zero for non-image objects
       *
       * @return the image height
       */
      public final int getImagePixHeight() {
          Preconditions.checkState(mImagePixHeight >= 0);
          return mImagePixHeight;
      }
  
      /**
       * Returns the height of the MTP object in pixels
       * Will be zero for non-image objects
       *
       * @return the image height
       */
      public final long getImagePixHeightLong() {
          return uint32ToLong(mImagePixHeight);
      }
  
      /**
       * Returns the depth of the MTP object in bits per pixel
       * Will be zero for non-image objects
       *
       * @return the image depth
       */
      public final int getImagePixDepth() {
          Preconditions.checkState(mImagePixDepth >= 0);
          return mImagePixDepth;
      }
  
      /**
       * Returns the depth of the MTP object in bits per pixel
       * Will be zero for non-image objects
       *
       * @return the image depth
       */
      public final long getImagePixDepthLong() {
          return uint32ToLong(mImagePixDepth);
      }
  
      /**
       * Returns the object handle for the object's parent
       * Will be zero for the root directory of a storage unit
       *
       * @return the object's parent
       */
      public final int getParent() {
          return mParent;
      }
  
      /**
       * Returns the association type for the MTP object
       * Will be zero objects that are not of format
       * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION}
       * For directories the association type is typically
       * {@link android.mtp.MtpConstants#ASSOCIATION_TYPE_GENERIC_FOLDER}
       *
       * @return the object's association type
       */
      public final int getAssociationType() {
          return mAssociationType;
      }
  
      /**
       * Returns the association description for the MTP object
       * Will be zero objects that are not of format
       * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION}
       *
       * @return the object's association description
       */
      public final int getAssociationDesc() {
          return mAssociationDesc;
      }
  
      /**
       * Returns the sequence number for the MTP object
       * This field is typically not used for MTP devices,
       * but is sometimes used to define a sequence of photos
       * on PTP cameras.
       *
       * @return the object's sequence number
       */
      public final int getSequenceNumber() {
          Preconditions.checkState(mSequenceNumber >= 0);
          return mSequenceNumber;
      }
  
      /**
       * Returns the sequence number for the MTP object
       * This field is typically not used for MTP devices,
       * but is sometimes used to define a sequence of photos
       * on PTP cameras.
       *
       * @return the object's sequence number
       */
      public final long getSequenceNumberLong() {
          return uint32ToLong(mSequenceNumber);
      }
  
     /**
       * Returns the name of the MTP object
       *
       * @return the object's name
       */
      public final @NonNull String getName() {
          return mName;
      }
  
     /**
       * Returns the creation date of the MTP object
       * The value is represented as milliseconds since January 1, 1970
       *
       * @return the object's creation date
       */
      public final long getDateCreated() {
          return mDateCreated;
      }
  
     /**
       * Returns the modification date of the MTP object
       * The value is represented as milliseconds since January 1, 1970
       *
       * @return the object's modification date
       */
      public final long getDateModified() {
          return mDateModified;
      }
  
     /**
       * Returns a comma separated list of keywords for the MTP object
       *
       * @return the object's keyword list
       */
      public final @NonNull String getKeywords() {
          return mKeywords;
      }
  
      /**
       * Builds a new object info instance.
       */
      public static class Builder {
          private MtpObjectInfo mObjectInfo;
  
          public Builder() {
              mObjectInfo = new MtpObjectInfo();
              mObjectInfo.mHandle = -1;
          }
  
          /**
           * Creates a builder on a copy of an existing object info.
           * All fields, except the object handle will be copied.
           *
           * @param objectInfo object info of an existing entry
           */
          public Builder(MtpObjectInfo objectInfo) {
              mObjectInfo = new MtpObjectInfo();
              mObjectInfo.mHandle = -1;
              mObjectInfo.mAssociationDesc = objectInfo.mAssociationDesc;
              mObjectInfo.mAssociationType = objectInfo.mAssociationType;
              mObjectInfo.mCompressedSize = objectInfo.mCompressedSize;
              mObjectInfo.mDateCreated = objectInfo.mDateCreated;
              mObjectInfo.mDateModified = objectInfo.mDateModified;
              mObjectInfo.mFormat = objectInfo.mFormat;
              mObjectInfo.mImagePixDepth = objectInfo.mImagePixDepth;
              mObjectInfo.mImagePixHeight = objectInfo.mImagePixHeight;
              mObjectInfo.mImagePixWidth = objectInfo.mImagePixWidth;
              mObjectInfo.mKeywords = objectInfo.mKeywords;
              mObjectInfo.mName = objectInfo.mName;
              mObjectInfo.mParent = objectInfo.mParent;
              mObjectInfo.mProtectionStatus = objectInfo.mProtectionStatus;
              mObjectInfo.mSequenceNumber = objectInfo.mSequenceNumber;
              mObjectInfo.mStorageId = objectInfo.mStorageId;
              mObjectInfo.mThumbCompressedSize = objectInfo.mThumbCompressedSize;
              mObjectInfo.mThumbFormat = objectInfo.mThumbFormat;
              mObjectInfo.mThumbPixHeight = objectInfo.mThumbPixHeight;
              mObjectInfo.mThumbPixWidth = objectInfo.mThumbPixWidth;
          }
  
          public Builder setObjectHandle(int value) {
              mObjectInfo.mHandle = value;
              return this;
          }
  
          public Builder setAssociationDesc(int value) {
              mObjectInfo.mAssociationDesc = value;
              return this;
          }
  
          public Builder setAssociationType(int value) {
              mObjectInfo.mAssociationType = value;
              return this;
          }
  
          public Builder setCompressedSize(long value) {
              mObjectInfo.mCompressedSize = longToUint32(value, "value");
              return this;
          }
  
          public Builder setDateCreated(long value) {
              mObjectInfo.mDateCreated = value;
              return this;
          }
  
          public Builder setDateModified(long value) {
              mObjectInfo.mDateModified = value;
              return this;
          }
  
          public Builder setFormat(int value) {
              mObjectInfo.mFormat = value;
              return this;
          }
  
          public Builder setImagePixDepth(long value) {
              mObjectInfo.mImagePixDepth = longToUint32(value, "value");
              return this;
          }
  
          public Builder setImagePixHeight(long value) {
              mObjectInfo.mImagePixHeight = longToUint32(value, "value");
              return this;
          }
  
          public Builder setImagePixWidth(long value) {
              mObjectInfo.mImagePixWidth = longToUint32(value, "value");
              return this;
          }
  
          public Builder setKeywords(@NonNull String value) {
              if (VMRuntime.getRuntime().getTargetSdkVersion() > Build.VERSION_CODES.N_MR1) {
                  Preconditions.checkNotNull(value);
              } else if (value == null) {
                  // Before N_MR1 we accept null value and it was regarded as an empty string in
                  // MtpDevice#sendObjectInfo.
                  value = "";
              }
              mObjectInfo.mKeywords = value;
              return this;
          }
  
          public Builder setName(@NonNull String value) {
              Preconditions.checkNotNull(value);
              mObjectInfo.mName = value;
              return this;
          }
  
          public Builder setParent(int value) {
              mObjectInfo.mParent = value;
              return this;
          }
  
          public Builder setProtectionStatus(int value) {
              mObjectInfo.mProtectionStatus = value;
              return this;
          }
  
          public Builder setSequenceNumber(long value) {
              mObjectInfo.mSequenceNumber = longToUint32(value, "value");
              return this;
          }
  
          public Builder setStorageId(int value) {
              mObjectInfo.mStorageId = value;
              return this;
          }
  
          public Builder setThumbCompressedSize(long value) {
              mObjectInfo.mThumbCompressedSize = longToUint32(value, "value");
              return this;
          }
  
          public Builder setThumbFormat(int value) {
              mObjectInfo.mThumbFormat = value;
              return this;
          }
  
          public Builder setThumbPixHeight(long value) {
              mObjectInfo.mThumbPixHeight = longToUint32(value, "value");
              return this;
          }
  
          public Builder setThumbPixWidth(long value) {
              mObjectInfo.mThumbPixWidth = longToUint32(value, "value");
              return this;
          }
  
          /**
           * Builds the object info instance. Once called, methods of the builder
           * must not be called anymore.
           *
           * @return the object info of the newly created file, or NULL in case
           *         of an error.
           */
          public MtpObjectInfo build() {
              MtpObjectInfo result = mObjectInfo;
              mObjectInfo = null;
              return result;
          }
      }
  
      private static long uint32ToLong(int value) {
          return value < 0 ? 0x100000000L + value : value;
      }
  
      private static int longToUint32(long value, String valueName) {
          Preconditions.checkArgumentInRange(value, 0, 0xffffffffL, valueName);
          return (int) value;
      }
  }
  