Home | History | Annotate | Download | only in print
      1 /*
      2  * Copyright (C) 2013 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.print;
     18 
     19 import android.annotation.IntDef;
     20 import android.annotation.IntRange;
     21 import android.annotation.NonNull;
     22 import android.annotation.Nullable;
     23 import android.annotation.StringRes;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.PackageManager.NameNotFoundException;
     26 import android.content.res.Resources.NotFoundException;
     27 import android.os.Parcel;
     28 import android.os.Parcelable;
     29 import android.text.TextUtils;
     30 import android.util.ArrayMap;
     31 import android.util.ArraySet;
     32 import android.util.Log;
     33 
     34 import com.android.internal.R;
     35 import com.android.internal.util.Preconditions;
     36 
     37 import java.lang.annotation.Retention;
     38 import java.lang.annotation.RetentionPolicy;
     39 import java.util.Map;
     40 
     41 /**
     42  * This class represents the attributes of a print job. These attributes
     43  * describe how the printed content should be laid out. For example, the
     44  * print attributes may state that the content should be laid out on a
     45  * letter size with 300 DPI (dots per inch) resolution, have a margin of
     46  * 10 mills (thousand of an inch) on all sides, and be black and white.
     47  */
     48 public final class PrintAttributes implements Parcelable {
     49     /** @hide */
     50     @Retention(RetentionPolicy.SOURCE)
     51     @IntDef(flag = true, value = {
     52             COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR
     53     })
     54     @interface ColorMode {
     55     }
     56     /** Color mode: Monochrome color scheme, for example one color is used. */
     57     public static final int COLOR_MODE_MONOCHROME = 1 << 0;
     58     /** Color mode: Color color scheme, for example many colors are used. */
     59     public static final int COLOR_MODE_COLOR = 1 << 1;
     60 
     61     private static final int VALID_COLOR_MODES =
     62             COLOR_MODE_MONOCHROME | COLOR_MODE_COLOR;
     63 
     64     /** @hide */
     65     @Retention(RetentionPolicy.SOURCE)
     66     @IntDef(flag = true, value = {
     67             DUPLEX_MODE_NONE, DUPLEX_MODE_LONG_EDGE, DUPLEX_MODE_SHORT_EDGE
     68     })
     69     @interface DuplexMode {
     70     }
     71     /** Duplex mode: No duplexing. */
     72     public static final int DUPLEX_MODE_NONE = 1 << 0;
     73     /** Duplex mode: Pages are turned sideways along the long edge - like a book. */
     74     public static final int DUPLEX_MODE_LONG_EDGE = 1 << 1;
     75     /** Duplex mode: Pages are turned upwards along the short edge - like a notpad. */
     76     public static final int DUPLEX_MODE_SHORT_EDGE = 1 << 2;
     77 
     78     private static final int VALID_DUPLEX_MODES =
     79             DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
     80 
     81     private @Nullable MediaSize mMediaSize;
     82     private @Nullable Resolution mResolution;
     83     private @Nullable Margins mMinMargins;
     84 
     85     private @IntRange(from = 0) int mColorMode;
     86     private @IntRange(from = 0) int mDuplexMode;
     87 
     88     PrintAttributes() {
     89         /* hide constructor */
     90     }
     91 
     92     private PrintAttributes(@NonNull Parcel parcel) {
     93         mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null;
     94         mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null;
     95         mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
     96         mColorMode = parcel.readInt();
     97         if (mColorMode != 0) {
     98             enforceValidColorMode(mColorMode);
     99         }
    100         mDuplexMode = parcel.readInt();
    101         if (mDuplexMode != 0) {
    102             enforceValidDuplexMode(mDuplexMode);
    103         }
    104     }
    105 
    106     /**
    107      * Gets the media size.
    108      *
    109      * @return The media size or <code>null</code> if not set.
    110      */
    111     public @Nullable MediaSize getMediaSize() {
    112         return mMediaSize;
    113     }
    114 
    115     /**
    116      * Sets the media size.
    117      *
    118      * @param mediaSize The media size.
    119      *
    120      * @hide
    121      */
    122     public void setMediaSize(MediaSize mediaSize) {
    123         mMediaSize = mediaSize;
    124     }
    125 
    126     /**
    127      * Gets the resolution.
    128      *
    129      * @return The resolution or <code>null</code> if not set.
    130      */
    131     public @Nullable Resolution getResolution() {
    132         return mResolution;
    133     }
    134 
    135     /**
    136      * Sets the resolution.
    137      *
    138      * @param resolution The resolution.
    139      *
    140      * @hide
    141      */
    142     public void setResolution(Resolution resolution) {
    143         mResolution = resolution;
    144     }
    145 
    146     /**
    147      * Gets the minimal margins. If the content does not fit
    148      * these margins it will be clipped.
    149      * <p>
    150      * <strong>These margins are physically imposed by the printer and they
    151      * are <em>not</em> rotated, i.e. they are the same for both portrait and
    152      * landscape. For example, a printer may not be able to print in a stripe
    153      * on both left and right sides of the page.
    154      * </strong>
    155      * </p>
    156      *
    157      * @return The margins or <code>null</code> if not set.
    158      */
    159     public @Nullable Margins getMinMargins() {
    160         return mMinMargins;
    161     }
    162 
    163     /**
    164      * Sets the minimal margins. If the content does not fit
    165      * these margins it will be clipped.
    166      * <p>
    167      * <strong>These margins are physically imposed by the printer and they
    168      * are <em>not</em> rotated, i.e. they are the same for both portrait and
    169      * landscape. For example, a printer may not be able to print in a stripe
    170      * on both left and right sides of the page.
    171      * </strong>
    172      * </p>
    173      *
    174      * @param margins The margins.
    175      *
    176      * @hide
    177      */
    178     public void setMinMargins(Margins margins) {
    179         mMinMargins = margins;
    180     }
    181 
    182     /**
    183      * Gets the color mode.
    184      *
    185      * @return The color mode or zero if not set.
    186      *
    187      * @see #COLOR_MODE_COLOR
    188      * @see #COLOR_MODE_MONOCHROME
    189      */
    190     public @IntRange(from = 0) int getColorMode() {
    191         return mColorMode;
    192     }
    193 
    194     /**
    195      * Sets the color mode.
    196      *
    197      * @param colorMode The color mode.
    198      *
    199      * @see #COLOR_MODE_MONOCHROME
    200      * @see #COLOR_MODE_COLOR
    201      *
    202      * @hide
    203      */
    204     public void setColorMode(int colorMode) {
    205         enforceValidColorMode(colorMode);
    206         mColorMode = colorMode;
    207     }
    208 
    209     /**
    210      * Gets whether this print attributes are in portrait orientation,
    211      * which is the media size is in portrait and all orientation dependent
    212      * attributes such as resolution and margins are properly adjusted.
    213      *
    214      * @return Whether this print attributes are in portrait.
    215      *
    216      * @hide
    217      */
    218     public boolean isPortrait() {
    219         return mMediaSize.isPortrait();
    220     }
    221 
    222     /**
    223      * Gets the duplex mode.
    224      *
    225      * @return The duplex mode or zero if not set.
    226      *
    227      * @see #DUPLEX_MODE_NONE
    228      * @see #DUPLEX_MODE_LONG_EDGE
    229      * @see #DUPLEX_MODE_SHORT_EDGE
    230      */
    231     public @IntRange(from = 0) int getDuplexMode() {
    232         return mDuplexMode;
    233     }
    234 
    235     /**
    236      * Sets the duplex mode.
    237      *
    238      * @param duplexMode The duplex mode.
    239      *
    240      * @see #DUPLEX_MODE_NONE
    241      * @see #DUPLEX_MODE_LONG_EDGE
    242      * @see #DUPLEX_MODE_SHORT_EDGE
    243      *
    244      * @hide
    245      */
    246     public void setDuplexMode(int duplexMode) {
    247         enforceValidDuplexMode(duplexMode);
    248         mDuplexMode = duplexMode;
    249     }
    250 
    251     /**
    252      * Gets a new print attributes instance which is in portrait orientation,
    253      * which is the media size is in portrait and all orientation dependent
    254      * attributes such as resolution and margins are properly adjusted.
    255      *
    256      * @return New instance in portrait orientation if this one is in
    257      * landscape, otherwise this instance.
    258      *
    259      * @hide
    260      */
    261     public PrintAttributes asPortrait() {
    262         if (isPortrait()) {
    263             return this;
    264         }
    265 
    266         PrintAttributes attributes = new PrintAttributes();
    267 
    268         // Rotate the media size.
    269         attributes.setMediaSize(getMediaSize().asPortrait());
    270 
    271         // Rotate the resolution.
    272         Resolution oldResolution = getResolution();
    273         Resolution newResolution = new Resolution(
    274                 oldResolution.getId(),
    275                 oldResolution.getLabel(),
    276                 oldResolution.getVerticalDpi(),
    277                 oldResolution.getHorizontalDpi());
    278         attributes.setResolution(newResolution);
    279 
    280         // Do not rotate the physical margins.
    281         attributes.setMinMargins(getMinMargins());
    282 
    283         attributes.setColorMode(getColorMode());
    284         attributes.setDuplexMode(getDuplexMode());
    285 
    286         return attributes;
    287     }
    288 
    289     /**
    290      * Gets a new print attributes instance which is in landscape orientation,
    291      * which is the media size is in landscape and all orientation dependent
    292      * attributes such as resolution and margins are properly adjusted.
    293      *
    294      * @return New instance in landscape orientation if this one is in
    295      * portrait, otherwise this instance.
    296      *
    297      * @hide
    298      */
    299     public PrintAttributes asLandscape() {
    300         if (!isPortrait()) {
    301             return this;
    302         }
    303 
    304         PrintAttributes attributes = new PrintAttributes();
    305 
    306         // Rotate the media size.
    307         attributes.setMediaSize(getMediaSize().asLandscape());
    308 
    309         // Rotate the resolution.
    310         Resolution oldResolution = getResolution();
    311         Resolution newResolution = new Resolution(
    312                 oldResolution.getId(),
    313                 oldResolution.getLabel(),
    314                 oldResolution.getVerticalDpi(),
    315                 oldResolution.getHorizontalDpi());
    316         attributes.setResolution(newResolution);
    317 
    318         // Do not rotate the physical margins.
    319         attributes.setMinMargins(getMinMargins());
    320 
    321         attributes.setColorMode(getColorMode());
    322         attributes.setDuplexMode(getDuplexMode());
    323 
    324         return attributes;
    325     }
    326 
    327     @Override
    328     public void writeToParcel(Parcel parcel, int flags) {
    329         if (mMediaSize != null) {
    330             parcel.writeInt(1);
    331             mMediaSize.writeToParcel(parcel);
    332         } else {
    333             parcel.writeInt(0);
    334         }
    335         if (mResolution != null) {
    336             parcel.writeInt(1);
    337             mResolution.writeToParcel(parcel);
    338         } else {
    339             parcel.writeInt(0);
    340         }
    341         if (mMinMargins != null) {
    342             parcel.writeInt(1);
    343             mMinMargins.writeToParcel(parcel);
    344         } else {
    345             parcel.writeInt(0);
    346         }
    347         parcel.writeInt(mColorMode);
    348         parcel.writeInt(mDuplexMode);
    349     }
    350 
    351     @Override
    352     public int describeContents() {
    353         return 0;
    354     }
    355 
    356     @Override
    357     public int hashCode() {
    358         final int prime = 31;
    359         int result = 1;
    360         result = prime * result + mColorMode;
    361         result = prime * result + mDuplexMode;
    362         result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
    363         result = prime * result + ((mMediaSize == null) ? 0 : mMediaSize.hashCode());
    364         result = prime * result + ((mResolution == null) ? 0 : mResolution.hashCode());
    365         return result;
    366     }
    367 
    368     @Override
    369     public boolean equals(Object obj) {
    370         if (this == obj) {
    371             return true;
    372         }
    373         if (obj == null) {
    374             return false;
    375         }
    376         if (getClass() != obj.getClass()) {
    377             return false;
    378         }
    379         PrintAttributes other = (PrintAttributes) obj;
    380         if (mColorMode != other.mColorMode) {
    381             return false;
    382         }
    383         if (mDuplexMode != other.mDuplexMode) {
    384             return false;
    385         }
    386         if (mMinMargins == null) {
    387             if (other.mMinMargins != null) {
    388                 return false;
    389             }
    390         } else if (!mMinMargins.equals(other.mMinMargins)) {
    391             return false;
    392         }
    393         if (mMediaSize == null) {
    394             if (other.mMediaSize != null) {
    395                 return false;
    396             }
    397         } else if (!mMediaSize.equals(other.mMediaSize)) {
    398             return false;
    399         }
    400         if (mResolution == null) {
    401             if (other.mResolution != null) {
    402                 return false;
    403             }
    404         } else if (!mResolution.equals(other.mResolution)) {
    405             return false;
    406         }
    407         return true;
    408     }
    409 
    410     @Override
    411     public String toString() {
    412         StringBuilder builder = new StringBuilder();
    413         builder.append("PrintAttributes{");
    414         builder.append("mediaSize: ").append(mMediaSize);
    415         if (mMediaSize != null) {
    416             builder.append(", orientation: ").append(mMediaSize.isPortrait()
    417                     ? "portrait" : "landscape");
    418         } else {
    419             builder.append(", orientation: ").append("null");
    420         }
    421         builder.append(", resolution: ").append(mResolution);
    422         builder.append(", minMargins: ").append(mMinMargins);
    423         builder.append(", colorMode: ").append(colorModeToString(mColorMode));
    424         builder.append(", duplexMode: ").append(duplexModeToString(mDuplexMode));
    425         builder.append("}");
    426         return builder.toString();
    427     }
    428 
    429     /** @hide */
    430     public void clear() {
    431         mMediaSize = null;
    432         mResolution = null;
    433         mMinMargins = null;
    434         mColorMode = 0;
    435         mDuplexMode = 0;
    436     }
    437 
    438     /**
    439      * @hide
    440      */
    441     public void copyFrom(PrintAttributes other) {
    442         mMediaSize = other.mMediaSize;
    443         mResolution = other.mResolution;
    444         mMinMargins = other.mMinMargins;
    445         mColorMode = other.mColorMode;
    446         mDuplexMode = other.mDuplexMode;
    447     }
    448 
    449     /**
    450      * This class specifies a supported media size. Media size is the
    451      * dimension of the media on which the content is printed. For
    452      * example, the {@link #NA_LETTER} media size designates a page
    453      * with size 8.5" x 11".
    454      */
    455     public static final class MediaSize {
    456         private static final String LOG_TAG = "MediaSize";
    457 
    458         private static final Map<String, MediaSize> sIdToMediaSizeMap =
    459                 new ArrayMap<>();
    460 
    461         /**
    462          * Unknown media size in portrait mode.
    463          * <p>
    464          * <strong>Note: </strong>This is for specifying orientation without media
    465          * size. You should not use the dimensions reported by this instance.
    466          * </p>
    467          */
    468         public static final MediaSize UNKNOWN_PORTRAIT =
    469                 new MediaSize("UNKNOWN_PORTRAIT", "android",
    470                         R.string.mediasize_unknown_portrait, 1, Integer.MAX_VALUE);
    471 
    472         /**
    473          * Unknown media size in landscape mode.
    474          * <p>
    475          * <strong>Note: </strong>This is for specifying orientation without media
    476          * size. You should not use the dimensions reported by this instance.
    477          * </p>
    478          */
    479         public static final MediaSize UNKNOWN_LANDSCAPE =
    480                 new MediaSize("UNKNOWN_LANDSCAPE", "android",
    481                         R.string.mediasize_unknown_landscape, Integer.MAX_VALUE, 1);
    482 
    483         // ISO sizes
    484 
    485         /** ISO A0 media size: 841mm x 1189mm (33.11" x 46.81") */
    486         public static final MediaSize ISO_A0 =
    487                 new MediaSize("ISO_A0", "android", R.string.mediasize_iso_a0, 33110, 46810);
    488         /** ISO A1 media size: 594mm x 841mm (23.39" x 33.11") */
    489         public static final MediaSize ISO_A1 =
    490                 new MediaSize("ISO_A1", "android", R.string.mediasize_iso_a1, 23390, 33110);
    491         /** ISO A2 media size: 420mm x 594mm (16.54" x 23.39") */
    492         public static final MediaSize ISO_A2 =
    493                 new MediaSize("ISO_A2", "android", R.string.mediasize_iso_a2, 16540, 23390);
    494         /** ISO A3 media size: 297mm x 420mm (11.69" x 16.54") */
    495         public static final MediaSize ISO_A3 =
    496                 new MediaSize("ISO_A3", "android", R.string.mediasize_iso_a3, 11690, 16540);
    497         /** ISO A4 media size: 210mm x 297mm (8.27" x 11.69") */
    498         public static final MediaSize ISO_A4 =
    499                 new MediaSize("ISO_A4", "android", R.string.mediasize_iso_a4, 8270, 11690);
    500         /** ISO A5 media size: 148mm x 210mm (5.83" x 8.27") */
    501         public static final MediaSize ISO_A5 =
    502                 new MediaSize("ISO_A5", "android", R.string.mediasize_iso_a5, 5830, 8270);
    503         /** ISO A6 media size: 105mm x 148mm (4.13" x 5.83") */
    504         public static final MediaSize ISO_A6 =
    505                 new MediaSize("ISO_A6", "android", R.string.mediasize_iso_a6, 4130, 5830);
    506         /** ISO A7 media size: 74mm x 105mm (2.91" x 4.13") */
    507         public static final MediaSize ISO_A7 =
    508                 new MediaSize("ISO_A7", "android", R.string.mediasize_iso_a7, 2910, 4130);
    509         /** ISO A8 media size: 52mm x 74mm (2.05" x 2.91") */
    510         public static final MediaSize ISO_A8 =
    511                 new MediaSize("ISO_A8", "android", R.string.mediasize_iso_a8, 2050, 2910);
    512         /** ISO A9 media size: 37mm x 52mm (1.46" x 2.05") */
    513         public static final MediaSize ISO_A9 =
    514                 new MediaSize("ISO_A9", "android", R.string.mediasize_iso_a9, 1460, 2050);
    515         /** ISO A10 media size: 26mm x 37mm (1.02" x 1.46") */
    516         public static final MediaSize ISO_A10 =
    517                 new MediaSize("ISO_A10", "android", R.string.mediasize_iso_a10, 1020, 1460);
    518 
    519         /** ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67") */
    520         public static final MediaSize ISO_B0 =
    521                 new MediaSize("ISO_B0", "android", R.string.mediasize_iso_b0, 39370, 55670);
    522         /** ISO B1 media size: 707mm x 1000mm (27.83" x 39.37") */
    523         public static final MediaSize ISO_B1 =
    524                 new MediaSize("ISO_B1", "android", R.string.mediasize_iso_b1, 27830, 39370);
    525         /** ISO B2 media size: 500mm x 707mm (19.69" x 27.83") */
    526         public static final MediaSize ISO_B2 =
    527                 new MediaSize("ISO_B2", "android", R.string.mediasize_iso_b2, 19690, 27830);
    528         /** ISO B3 media size: 353mm x 500mm (13.90" x 19.69") */
    529         public static final MediaSize ISO_B3 =
    530                 new MediaSize("ISO_B3", "android", R.string.mediasize_iso_b3, 13900, 19690);
    531         /** ISO B4 media size: 250mm x 353mm (9.84" x 13.90") */
    532         public static final MediaSize ISO_B4 =
    533                 new MediaSize("ISO_B4", "android", R.string.mediasize_iso_b4, 9840, 13900);
    534         /** ISO B5 media size: 176mm x 250mm (6.93" x 9.84") */
    535         public static final MediaSize ISO_B5 =
    536                 new MediaSize("ISO_B5", "android", R.string.mediasize_iso_b5, 6930, 9840);
    537         /** ISO B6 media size: 125mm x 176mm (4.92" x 6.93") */
    538         public static final MediaSize ISO_B6 =
    539                 new MediaSize("ISO_B6", "android", R.string.mediasize_iso_b6, 4920, 6930);
    540         /** ISO B7 media size: 88mm x 125mm (3.46" x 4.92") */
    541         public static final MediaSize ISO_B7 =
    542                 new MediaSize("ISO_B7", "android", R.string.mediasize_iso_b7, 3460, 4920);
    543         /** ISO B8 media size: 62mm x 88mm (2.44" x 3.46") */
    544         public static final MediaSize ISO_B8 =
    545                 new MediaSize("ISO_B8", "android", R.string.mediasize_iso_b8, 2440, 3460);
    546         /** ISO B9 media size: 44mm x 62mm (1.73" x 2.44") */
    547         public static final MediaSize ISO_B9 =
    548                 new MediaSize("ISO_B9", "android", R.string.mediasize_iso_b9, 1730, 2440);
    549         /** ISO B10 media size: 31mm x 44mm (1.22" x 1.73") */
    550         public static final MediaSize ISO_B10 =
    551                 new MediaSize("ISO_B10", "android", R.string.mediasize_iso_b10, 1220, 1730);
    552 
    553         /** ISO C0 media size: 917mm x 1297mm (36.10" x 51.06") */
    554         public static final MediaSize ISO_C0 =
    555                 new MediaSize("ISO_C0", "android", R.string.mediasize_iso_c0, 36100, 51060);
    556         /** ISO C1 media size: 648mm x 917mm (25.51" x 36.10") */
    557         public static final MediaSize ISO_C1 =
    558                 new MediaSize("ISO_C1", "android", R.string.mediasize_iso_c1, 25510, 36100);
    559         /** ISO C2 media size: 458mm x 648mm (18.03" x 25.51") */
    560         public static final MediaSize ISO_C2 =
    561                 new MediaSize("ISO_C2", "android", R.string.mediasize_iso_c2, 18030, 25510);
    562         /** ISO C3 media size: 324mm x 458mm (12.76" x 18.03") */
    563         public static final MediaSize ISO_C3 =
    564                 new MediaSize("ISO_C3", "android", R.string.mediasize_iso_c3, 12760, 18030);
    565         /** ISO C4 media size: 229mm x 324mm (9.02" x 12.76") */
    566         public static final MediaSize ISO_C4 =
    567                 new MediaSize("ISO_C4", "android", R.string.mediasize_iso_c4, 9020, 12760);
    568         /** ISO C5 media size: 162mm x 229mm (6.38" x 9.02") */
    569         public static final MediaSize ISO_C5 =
    570                 new MediaSize("ISO_C5", "android", R.string.mediasize_iso_c5, 6380, 9020);
    571         /** ISO C6 media size: 114mm x 162mm (4.49" x 6.38") */
    572         public static final MediaSize ISO_C6 =
    573                 new MediaSize("ISO_C6", "android", R.string.mediasize_iso_c6, 4490, 6380);
    574         /** ISO C7 media size: 81mm x 114mm (3.19" x 4.49") */
    575         public static final MediaSize ISO_C7 =
    576                 new MediaSize("ISO_C7", "android", R.string.mediasize_iso_c7, 3190, 4490);
    577         /** ISO C8 media size: 57mm x 81mm (2.24" x 3.19") */
    578         public static final MediaSize ISO_C8 =
    579                 new MediaSize("ISO_C8", "android", R.string.mediasize_iso_c8, 2240, 3190);
    580         /** ISO C9 media size: 40mm x 57mm (1.57" x 2.24") */
    581         public static final MediaSize ISO_C9 =
    582                 new MediaSize("ISO_C9", "android", R.string.mediasize_iso_c9, 1570, 2240);
    583         /** ISO C10 media size: 28mm x 40mm (1.10" x 1.57") */
    584         public static final MediaSize ISO_C10 =
    585                 new MediaSize("ISO_C10", "android", R.string.mediasize_iso_c10, 1100, 1570);
    586 
    587         // North America
    588 
    589         /** North America Letter media size: 8.5" x 11" (279mm x 216mm) */
    590         public static final MediaSize NA_LETTER =
    591                 new MediaSize("NA_LETTER", "android", R.string.mediasize_na_letter, 8500, 11000);
    592         /** North America Government-Letter media size: 8.0" x 10.5" (203mm x 267mm) */
    593         public static final MediaSize NA_GOVT_LETTER =
    594                 new MediaSize("NA_GOVT_LETTER", "android",
    595                         R.string.mediasize_na_gvrnmt_letter, 8000, 10500);
    596         /** North America Legal media size: 8.5" x 14" (216mm x 356mm) */
    597         public static final MediaSize NA_LEGAL =
    598                 new MediaSize("NA_LEGAL", "android", R.string.mediasize_na_legal, 8500, 14000);
    599         /** North America Junior Legal media size: 8.0" x 5.0" (203mm  127mm) */
    600         public static final MediaSize NA_JUNIOR_LEGAL =
    601                 new MediaSize("NA_JUNIOR_LEGAL", "android",
    602                         R.string.mediasize_na_junior_legal, 8000, 5000);
    603         /** North America Ledger media size: 17" x 11" (432mm  279mm) */
    604         public static final MediaSize NA_LEDGER =
    605                 new MediaSize("NA_LEDGER", "android", R.string.mediasize_na_ledger, 17000, 11000);
    606         /** North America Tabloid media size: 11" x 17" (279mm  432mm) */
    607         public static final MediaSize NA_TABLOID =
    608                 new MediaSize("NA_TABLOID", "android",
    609                         R.string.mediasize_na_tabloid, 11000, 17000);
    610         /** North America Index Card 3x5 media size: 3" x 5" (76mm x 127mm) */
    611         public static final MediaSize NA_INDEX_3X5 =
    612                 new MediaSize("NA_INDEX_3X5", "android",
    613                         R.string.mediasize_na_index_3x5, 3000, 5000);
    614         /** North America Index Card 4x6 media size: 4" x 6" (102mm x 152mm) */
    615         public static final MediaSize NA_INDEX_4X6 =
    616                 new MediaSize("NA_INDEX_4X6", "android",
    617                         R.string.mediasize_na_index_4x6, 4000, 6000);
    618         /** North America Index Card 5x8 media size: 5" x 8" (127mm x 203mm) */
    619         public static final MediaSize NA_INDEX_5X8 =
    620                 new MediaSize("NA_INDEX_5X8", "android",
    621                         R.string.mediasize_na_index_5x8, 5000, 8000);
    622         /** North America Monarch media size: 7.25" x 10.5" (184mm x 267mm) */
    623         public static final MediaSize NA_MONARCH =
    624                 new MediaSize("NA_MONARCH", "android",
    625                         R.string.mediasize_na_monarch, 7250, 10500);
    626         /** North America Quarto media size: 8" x 10" (203mm x 254mm) */
    627         public static final MediaSize NA_QUARTO =
    628                 new MediaSize("NA_QUARTO", "android",
    629                         R.string.mediasize_na_quarto, 8000, 10000);
    630         /** North America Foolscap media size: 8" x 13" (203mm x 330mm) */
    631         public static final MediaSize NA_FOOLSCAP =
    632                 new MediaSize("NA_FOOLSCAP", "android",
    633                         R.string.mediasize_na_foolscap, 8000, 13000);
    634 
    635         // Chinese
    636 
    637         /** Chinese ROC 8K media size: 270mm x 390mm (10.629" x 15.3543") */
    638         public static final MediaSize ROC_8K =
    639                 new MediaSize("ROC_8K", "android",
    640                         R.string.mediasize_chinese_roc_8k, 10629, 15354);
    641         /** Chinese ROC 16K media size: 195mm x 270mm (7.677" x 10.629") */
    642         public static final MediaSize ROC_16K =
    643                 new MediaSize("ROC_16K", "android",
    644                         R.string.mediasize_chinese_roc_16k, 7677, 10629);
    645 
    646         /** Chinese PRC 1 media size: 102mm x 165mm (4.015" x 6.496") */
    647         public static final MediaSize PRC_1 =
    648                 new MediaSize("PRC_1", "android",
    649                         R.string.mediasize_chinese_prc_1, 4015, 6496);
    650         /** Chinese PRC 2 media size: 102mm x 176mm (4.015" x 6.929") */
    651         public static final MediaSize PRC_2 =
    652                 new MediaSize("PRC_2", "android",
    653                         R.string.mediasize_chinese_prc_2, 4015, 6929);
    654         /** Chinese PRC 3 media size: 125mm x 176mm (4.921" x 6.929") */
    655         public static final MediaSize PRC_3 =
    656                 new MediaSize("PRC_3", "android",
    657                         R.string.mediasize_chinese_prc_3, 4921, 6929);
    658         /** Chinese PRC 4 media size: 110mm x 208mm (4.330" x 8.189") */
    659         public static final MediaSize PRC_4 =
    660                 new MediaSize("PRC_4", "android",
    661                         R.string.mediasize_chinese_prc_4, 4330, 8189);
    662         /** Chinese PRC 5 media size: 110mm x 220mm (4.330" x 8.661") */
    663         public static final MediaSize PRC_5 =
    664                 new MediaSize("PRC_5", "android",
    665                         R.string.mediasize_chinese_prc_5, 4330, 8661);
    666         /** Chinese PRC 6 media size: 120mm x 320mm (4.724" x 12.599") */
    667         public static final MediaSize PRC_6 =
    668                 new MediaSize("PRC_6", "android",
    669                         R.string.mediasize_chinese_prc_6, 4724, 12599);
    670         /** Chinese PRC 7 media size: 160mm x 230mm (6.299" x 9.055") */
    671         public static final MediaSize PRC_7 =
    672                 new MediaSize("PRC_7", "android",
    673                         R.string.mediasize_chinese_prc_7, 6299, 9055);
    674         /** Chinese PRC 8 media size: 120mm x 309mm (4.724" x 12.165") */
    675         public static final MediaSize PRC_8 =
    676                 new MediaSize("PRC_8", "android",
    677                         R.string.mediasize_chinese_prc_8, 4724, 12165);
    678         /** Chinese PRC 9 media size: 229mm x 324mm (9.016" x 12.756") */
    679         public static final MediaSize PRC_9 =
    680                 new MediaSize("PRC_9", "android",
    681                         R.string.mediasize_chinese_prc_9, 9016, 12756);
    682         /** Chinese PRC 10 media size: 324mm x 458mm (12.756" x 18.032") */
    683         public static final MediaSize PRC_10 =
    684                 new MediaSize("PRC_10", "android",
    685                         R.string.mediasize_chinese_prc_10, 12756, 18032);
    686 
    687         /** Chinese PRC 16k media size: 146mm x 215mm (5.749" x 8.465") */
    688         public static final MediaSize PRC_16K =
    689                 new MediaSize("PRC_16K", "android",
    690                         R.string.mediasize_chinese_prc_16k, 5749, 8465);
    691         /** Chinese Pa Kai media size: 267mm x 389mm (10.512" x 15.315") */
    692         public static final MediaSize OM_PA_KAI =
    693                 new MediaSize("OM_PA_KAI", "android",
    694                         R.string.mediasize_chinese_om_pa_kai, 10512, 15315);
    695         /** Chinese Dai Pa Kai media size: 275mm x 395mm (10.827" x 15.551") */
    696         public static final MediaSize OM_DAI_PA_KAI =
    697                 new MediaSize("OM_DAI_PA_KAI", "android",
    698                         R.string.mediasize_chinese_om_dai_pa_kai, 10827, 15551);
    699         /** Chinese Jurro Ku Kai media size: 198mm x 275mm (7.796" x 10.827") */
    700         public static final MediaSize OM_JUURO_KU_KAI =
    701                 new MediaSize("OM_JUURO_KU_KAI", "android",
    702                         R.string.mediasize_chinese_om_jurro_ku_kai, 7796, 10827);
    703 
    704         // Japanese
    705 
    706         /** Japanese JIS B10 media size: 32mm x 45mm (1.259" x 1.772") */
    707         public static final MediaSize JIS_B10 =
    708                 new MediaSize("JIS_B10", "android",
    709                         R.string.mediasize_japanese_jis_b10, 1259, 1772);
    710         /** Japanese JIS B9 media size: 45mm x 64mm (1.772" x 2.52") */
    711         public static final MediaSize JIS_B9 =
    712                 new MediaSize("JIS_B9", "android",
    713                         R.string.mediasize_japanese_jis_b9, 1772, 2520);
    714         /** Japanese JIS B8 media size: 64mm x 91mm (2.52" x 3.583") */
    715         public static final MediaSize JIS_B8 =
    716                 new MediaSize("JIS_B8", "android",
    717                         R.string.mediasize_japanese_jis_b8, 2520, 3583);
    718         /** Japanese JIS B7 media size: 91mm x 128mm (3.583" x 5.049") */
    719         public static final MediaSize JIS_B7 =
    720                 new MediaSize("JIS_B7", "android",
    721                         R.string.mediasize_japanese_jis_b7, 3583, 5049);
    722         /** Japanese JIS B6 media size: 128mm x 182mm (5.049" x 7.165") */
    723         public static final MediaSize JIS_B6 =
    724                 new MediaSize("JIS_B6", "android",
    725                         R.string.mediasize_japanese_jis_b6, 5049, 7165);
    726         /** Japanese JIS B5 media size: 182mm x 257mm (7.165" x 10.118") */
    727         public static final MediaSize JIS_B5 =
    728                 new MediaSize("JIS_B5", "android",
    729                         R.string.mediasize_japanese_jis_b5, 7165, 10118);
    730         /** Japanese JIS B4 media size: 257mm x 364mm (10.118" x 14.331") */
    731         public static final MediaSize JIS_B4 =
    732                 new MediaSize("JIS_B4", "android",
    733                         R.string.mediasize_japanese_jis_b4, 10118, 14331);
    734         /** Japanese JIS B3 media size: 364mm x 515mm (14.331" x 20.276") */
    735         public static final MediaSize JIS_B3 =
    736                 new MediaSize("JIS_B3", "android",
    737                         R.string.mediasize_japanese_jis_b3, 14331, 20276);
    738         /** Japanese JIS B2 media size: 515mm x 728mm (20.276" x 28.661") */
    739         public static final MediaSize JIS_B2 =
    740                 new MediaSize("JIS_B2", "android",
    741                         R.string.mediasize_japanese_jis_b2, 20276, 28661);
    742         /** Japanese JIS B1 media size: 728mm x 1030mm (28.661" x 40.551") */
    743         public static final MediaSize JIS_B1 =
    744                 new MediaSize("JIS_B1", "android",
    745                         R.string.mediasize_japanese_jis_b1, 28661, 40551);
    746         /** Japanese JIS B0 media size: 1030mm x 1456mm (40.551" x 57.323") */
    747         public static final MediaSize JIS_B0 =
    748                 new MediaSize("JIS_B0", "android",
    749                         R.string.mediasize_japanese_jis_b0, 40551, 57323);
    750 
    751         /** Japanese JIS Exec media size: 216mm x 330mm (8.504" x 12.992") */
    752         public static final MediaSize JIS_EXEC =
    753                 new MediaSize("JIS_EXEC", "android",
    754                         R.string.mediasize_japanese_jis_exec, 8504, 12992);
    755 
    756         /** Japanese Chou4 media size: 90mm x 205mm (3.543" x 8.071") */
    757         public static final MediaSize JPN_CHOU4 =
    758                 new MediaSize("JPN_CHOU4", "android",
    759                         R.string.mediasize_japanese_chou4, 3543, 8071);
    760         /** Japanese Chou3 media size: 120mm x 235mm (4.724" x 9.252") */
    761         public static final MediaSize JPN_CHOU3 =
    762                 new MediaSize("JPN_CHOU3", "android",
    763                         R.string.mediasize_japanese_chou3, 4724, 9252);
    764         /** Japanese Chou2 media size: 111.1mm x 146mm (4.374" x 5.748") */
    765         public static final MediaSize JPN_CHOU2 =
    766                 new MediaSize("JPN_CHOU2", "android",
    767                         R.string.mediasize_japanese_chou2, 4374, 5748);
    768 
    769         /** Japanese Hagaki media size: 100mm x 148mm (3.937" x 5.827") */
    770         public static final MediaSize JPN_HAGAKI =
    771                 new MediaSize("JPN_HAGAKI", "android",
    772                         R.string.mediasize_japanese_hagaki, 3937, 5827);
    773         /** Japanese Oufuku media size: 148mm x 200mm (5.827" x 7.874") */
    774         public static final MediaSize JPN_OUFUKU =
    775                 new MediaSize("JPN_OUFUKU", "android",
    776                         R.string.mediasize_japanese_oufuku, 5827, 7874);
    777 
    778         /** Japanese Kahu media size: 240mm x 322.1mm (9.449" x 12.681") */
    779         public static final MediaSize JPN_KAHU =
    780                 new MediaSize("JPN_KAHU", "android",
    781                         R.string.mediasize_japanese_kahu, 9449, 12681);
    782         /** Japanese Kaku2 media size: 240mm x 332mm (9.449" x 13.071") */
    783         public static final MediaSize JPN_KAKU2 =
    784                 new MediaSize("JPN_KAKU2", "android",
    785                         R.string.mediasize_japanese_kaku2, 9449, 13071);
    786 
    787         /** Japanese You4 media size: 105mm x 235mm (4.134" x 9.252") */
    788         public static final MediaSize JPN_YOU4 =
    789                 new MediaSize("JPN_YOU4", "android",
    790                         R.string.mediasize_japanese_you4, 4134, 9252);
    791 
    792         private final @NonNull String mId;
    793         /**@hide */
    794         public final @NonNull String mLabel;
    795         /**@hide */
    796         public final @Nullable String mPackageName;
    797         /**@hide */
    798         public final @StringRes int mLabelResId;
    799         private final @IntRange(from = 1) int mWidthMils;
    800         private final @IntRange(from = 1) int mHeightMils;
    801 
    802         /**
    803          * Creates a new instance.
    804          *
    805          * @param id The unique media size id.
    806          * @param packageName The name of the creating package.
    807          * @param labelResId The resource if of a human readable label.
    808          * @param widthMils The width in mils (thousandths of an inch).
    809          * @param heightMils The height in mils (thousandths of an inch).
    810          *
    811          * @throws IllegalArgumentException If the id is empty or the label
    812          * is empty or the widthMils is less than or equal to zero or the
    813          * heightMils is less than or equal to zero.
    814          *
    815          * @hide
    816          */
    817         public MediaSize(String id, String packageName, int labelResId,
    818                 int widthMils, int heightMils) {
    819             this(id, null, packageName, widthMils, heightMils, labelResId);
    820 
    821             // Build this mapping only for predefined media sizes.
    822             sIdToMediaSizeMap.put(mId, this);
    823         }
    824 
    825         /**
    826          * Creates a new instance.
    827          *
    828          * @param id The unique media size id. It is unique amongst other media sizes
    829          *        supported by the printer.
    830          * @param label The <strong>localized</strong> human readable label.
    831          * @param widthMils The width in mils (thousandths of an inch).
    832          * @param heightMils The height in mils (thousandths of an inch).
    833          *
    834          * @throws IllegalArgumentException If the id is empty or the label is empty
    835          * or the widthMils is less than or equal to zero or the heightMils is less
    836          * than or equal to zero.
    837          */
    838         public MediaSize(@NonNull String id, @NonNull String label,
    839                 @IntRange(from = 1) int widthMils, @IntRange(from = 1) int heightMils) {
    840             this(id, label, null, widthMils, heightMils, 0);
    841         }
    842 
    843         /**
    844          * Get the Id of all predefined media sizes beside the {@link #UNKNOWN_PORTRAIT} and
    845          * {@link #UNKNOWN_LANDSCAPE}.
    846          *
    847          * @return List of all predefined media sizes
    848          *
    849          * @hide
    850          */
    851         public static @NonNull ArraySet<MediaSize> getAllPredefinedSizes() {
    852             ArraySet<MediaSize> definedMediaSizes = new ArraySet<>(sIdToMediaSizeMap.values());
    853 
    854             definedMediaSizes.remove(UNKNOWN_PORTRAIT);
    855             definedMediaSizes.remove(UNKNOWN_LANDSCAPE);
    856 
    857             return definedMediaSizes;
    858         }
    859 
    860         /**
    861          * Creates a new instance.
    862          *
    863          * @param id The unique media size id. It is unique amongst other media sizes
    864          *        supported by the printer.
    865          * @param label The <strong>localized</strong> human readable label.
    866          * @param packageName The name of the creating package.
    867          * @param widthMils The width in mils (thousandths of an inch).
    868          * @param heightMils The height in mils (thousandths of an inch).
    869          * @param labelResId The resource if of a human readable label.
    870          *
    871          * @throws IllegalArgumentException If the id is empty or the label is unset
    872          * or the widthMils is less than or equal to zero or the heightMils is less
    873          * than or equal to zero.
    874          *
    875          * @hide
    876          */
    877         public MediaSize(String id, String label, String packageName, int widthMils, int heightMils,
    878                 int labelResId) {
    879             mPackageName = packageName;
    880             mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty.");
    881             mLabelResId = labelResId;
    882             mWidthMils = Preconditions.checkArgumentPositive(widthMils, "widthMils cannot be " +
    883                     "less than or equal to zero.");
    884             mHeightMils = Preconditions.checkArgumentPositive(heightMils, "heightMils cannot be " +
    885                     "less than or equal to zero.");
    886             mLabel = label;
    887 
    888             // The label has to be either a string ot a StringRes
    889             Preconditions.checkArgument(!TextUtils.isEmpty(label) !=
    890                     (!TextUtils.isEmpty(packageName) && labelResId != 0), "label cannot be empty.");
    891         }
    892 
    893         /**
    894          * Gets the unique media size id. It is unique amongst other media sizes
    895          * supported by the printer.
    896          * <p>
    897          * This id is defined by the client that generated the media size
    898          * instance and should not be interpreted by other parties.
    899          * </p>
    900          *
    901          * @return The unique media size id.
    902          */
    903         public @NonNull String getId() {
    904             return mId;
    905         }
    906 
    907         /**
    908          * Gets the human readable media size label.
    909          *
    910          * @param packageManager The package manager for loading the label.
    911          * @return The human readable label.
    912          */
    913         public @NonNull String getLabel(@NonNull PackageManager packageManager) {
    914             if (!TextUtils.isEmpty(mPackageName) && mLabelResId > 0) {
    915                 try {
    916                     return packageManager.getResourcesForApplication(
    917                             mPackageName).getString(mLabelResId);
    918                 } catch (NotFoundException | NameNotFoundException e) {
    919                     Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
    920                             + " from package " + mPackageName);
    921                 }
    922             }
    923             return mLabel;
    924         }
    925 
    926         /**
    927          * Gets the media width in mils (thousandths of an inch).
    928          *
    929          * @return The media width.
    930          */
    931         public @IntRange(from = 1) int getWidthMils() {
    932             return mWidthMils;
    933         }
    934 
    935         /**
    936          * Gets the media height in mils (thousandths of an inch).
    937          *
    938          * @return The media height.
    939          */
    940         public @IntRange(from = 1) int getHeightMils() {
    941             return mHeightMils;
    942         }
    943 
    944         /**
    945          * Gets whether this media size is in portrait which is the
    946          * height is greater or equal to the width.
    947          *
    948          * @return True if the media size is in portrait, false if
    949          * it is in landscape.
    950          */
    951         public boolean isPortrait() {
    952             return mHeightMils >= mWidthMils;
    953         }
    954 
    955         /**
    956          * Returns a new media size instance in a portrait orientation,
    957          * which is the height is the greater dimension.
    958          *
    959          * @return New instance in landscape orientation if this one
    960          * is in landscape, otherwise this instance.
    961          */
    962         public @NonNull MediaSize asPortrait() {
    963             if (isPortrait()) {
    964                 return this;
    965             }
    966             return new MediaSize(mId, mLabel, mPackageName,
    967                     Math.min(mWidthMils, mHeightMils),
    968                     Math.max(mWidthMils, mHeightMils),
    969                     mLabelResId);
    970         }
    971 
    972         /**
    973          * Returns a new media size instance in a landscape orientation,
    974          * which is the height is the lesser dimension.
    975          *
    976          * @return New instance in landscape orientation if this one
    977          * is in portrait, otherwise this instance.
    978          */
    979         public @NonNull MediaSize asLandscape() {
    980             if (!isPortrait()) {
    981                 return this;
    982             }
    983             return new MediaSize(mId, mLabel, mPackageName,
    984                     Math.max(mWidthMils, mHeightMils),
    985                     Math.min(mWidthMils, mHeightMils),
    986                     mLabelResId);
    987         }
    988 
    989         void writeToParcel(Parcel parcel) {
    990             parcel.writeString(mId);
    991             parcel.writeString(mLabel);
    992             parcel.writeString(mPackageName);
    993             parcel.writeInt(mWidthMils);
    994             parcel.writeInt(mHeightMils);
    995             parcel.writeInt(mLabelResId);
    996         }
    997 
    998         static MediaSize createFromParcel(Parcel parcel) {
    999             return new MediaSize(
   1000                     parcel.readString(),
   1001                     parcel.readString(),
   1002                     parcel.readString(),
   1003                     parcel.readInt(),
   1004                     parcel.readInt(),
   1005                     parcel.readInt());
   1006         }
   1007 
   1008         @Override
   1009         public int hashCode() {
   1010             final int prime = 31;
   1011             int result = 1;
   1012             result = prime * result + mWidthMils;
   1013             result = prime * result + mHeightMils;
   1014             return result;
   1015         }
   1016 
   1017         @Override
   1018         public boolean equals(Object obj) {
   1019             if (this == obj) {
   1020                 return true;
   1021             }
   1022             if (obj == null) {
   1023                 return false;
   1024             }
   1025             if (getClass() != obj.getClass()) {
   1026                 return false;
   1027             }
   1028             MediaSize other = (MediaSize) obj;
   1029             if (mWidthMils != other.mWidthMils) {
   1030                 return false;
   1031             }
   1032             if (mHeightMils != other.mHeightMils) {
   1033                 return false;
   1034             }
   1035             return true;
   1036         }
   1037 
   1038         @Override
   1039         public String toString() {
   1040             StringBuilder builder = new StringBuilder();
   1041             builder.append("MediaSize{");
   1042             builder.append("id: ").append(mId);
   1043             builder.append(", label: ").append(mLabel);
   1044             builder.append(", packageName: ").append(mPackageName);
   1045             builder.append(", heightMils: ").append(mHeightMils);
   1046             builder.append(", widthMils: ").append(mWidthMils);
   1047             builder.append(", labelResId: ").append(mLabelResId);
   1048             builder.append("}");
   1049             return builder.toString();
   1050         }
   1051 
   1052         /**
   1053          * Gets a standard media size given its id.
   1054          *
   1055          * @param id The media size id.
   1056          * @return The media size for the given id or null.
   1057          *
   1058          * @hide
   1059          */
   1060         public static MediaSize getStandardMediaSizeById(String id) {
   1061             return sIdToMediaSizeMap.get(id);
   1062         }
   1063     }
   1064 
   1065     /**
   1066      * This class specifies a supported resolution in DPI (dots per inch).
   1067      * Resolution defines how many points with different color can be placed
   1068      * on one inch in horizontal or vertical direction of the target media.
   1069      * For example, a printer with 600 DPI can produce higher quality images
   1070      * the one with 300 DPI resolution.
   1071      */
   1072     public static final class Resolution {
   1073         private final @NonNull String mId;
   1074         private final @NonNull String mLabel;
   1075         private final @IntRange(from = 1) int mHorizontalDpi;
   1076         private final @IntRange(from = 1) int mVerticalDpi;
   1077 
   1078         /**
   1079          * Creates a new instance.
   1080          *
   1081          * @param id The unique resolution id. It is unique amongst other resolutions
   1082          *        supported by the printer.
   1083          * @param label The <strong>localized</strong> human readable label.
   1084          * @param horizontalDpi The horizontal resolution in DPI (dots per inch).
   1085          * @param verticalDpi The vertical resolution in DPI (dots per inch).
   1086          *
   1087          * @throws IllegalArgumentException If the id is empty or the label is empty
   1088          * or the horizontalDpi is less than or equal to zero or the verticalDpi is
   1089          * less than or equal to zero.
   1090          */
   1091         public Resolution(@NonNull String id, @NonNull String label,
   1092                 @IntRange(from = 1) int horizontalDpi, @IntRange(from = 1) int verticalDpi) {
   1093             if (TextUtils.isEmpty(id)) {
   1094                 throw new IllegalArgumentException("id cannot be empty.");
   1095             }
   1096             if (TextUtils.isEmpty(label)) {
   1097                 throw new IllegalArgumentException("label cannot be empty.");
   1098             }
   1099             if (horizontalDpi <= 0) {
   1100                 throw new IllegalArgumentException("horizontalDpi "
   1101                         + "cannot be less than or equal to zero.");
   1102             }
   1103             if (verticalDpi <= 0) {
   1104                 throw new IllegalArgumentException("verticalDpi"
   1105                        + " cannot be less than or equal to zero.");
   1106             }
   1107             mId = id;
   1108             mLabel = label;
   1109             mHorizontalDpi = horizontalDpi;
   1110             mVerticalDpi = verticalDpi;
   1111         }
   1112 
   1113         /**
   1114          * Gets the unique resolution id. It is unique amongst other resolutions
   1115          * supported by the printer.
   1116          * <p>
   1117          * This id is defined by the client that generated the resolution
   1118          * instance and should not be interpreted by other parties.
   1119          * </p>
   1120          *
   1121          * @return The unique resolution id.
   1122          */
   1123         public @NonNull String getId() {
   1124             return mId;
   1125         }
   1126 
   1127         /**
   1128          * Gets the resolution human readable label.
   1129          *
   1130          * @return The human readable label.
   1131          */
   1132         public @NonNull String getLabel() {
   1133             return mLabel;
   1134         }
   1135 
   1136         /**
   1137          * Gets the horizontal resolution in DPI (dots per inch).
   1138          *
   1139          * @return The horizontal resolution.
   1140          */
   1141         public @IntRange(from = 1) int getHorizontalDpi() {
   1142             return mHorizontalDpi;
   1143         }
   1144 
   1145         /**
   1146          * Gets the vertical resolution in DPI (dots per inch).
   1147          *
   1148          * @return The vertical resolution.
   1149          */
   1150         public @IntRange(from = 1) int getVerticalDpi() {
   1151             return mVerticalDpi;
   1152         }
   1153 
   1154         void writeToParcel(Parcel parcel) {
   1155             parcel.writeString(mId);
   1156             parcel.writeString(mLabel);
   1157             parcel.writeInt(mHorizontalDpi);
   1158             parcel.writeInt(mVerticalDpi);
   1159         }
   1160 
   1161         static Resolution createFromParcel(Parcel parcel) {
   1162             return new Resolution(
   1163                     parcel.readString(),
   1164                     parcel.readString(),
   1165                     parcel.readInt(),
   1166                     parcel.readInt());
   1167         }
   1168 
   1169         @Override
   1170         public int hashCode() {
   1171             final int prime = 31;
   1172             int result = 1;
   1173             result = prime * result + mHorizontalDpi;
   1174             result = prime * result + mVerticalDpi;
   1175             return result;
   1176         }
   1177 
   1178         @Override
   1179         public boolean equals(Object obj) {
   1180             if (this == obj) {
   1181                 return true;
   1182             }
   1183             if (obj == null) {
   1184                 return false;
   1185             }
   1186             if (getClass() != obj.getClass()) {
   1187                 return false;
   1188             }
   1189             Resolution other = (Resolution) obj;
   1190             if (mHorizontalDpi != other.mHorizontalDpi) {
   1191                 return false;
   1192             }
   1193             if (mVerticalDpi != other.mVerticalDpi) {
   1194                 return false;
   1195             }
   1196             return true;
   1197         }
   1198 
   1199         @Override
   1200         public String toString() {
   1201             StringBuilder builder = new StringBuilder();
   1202             builder.append("Resolution{");
   1203             builder.append("id: ").append(mId);
   1204             builder.append(", label: ").append(mLabel);
   1205             builder.append(", horizontalDpi: ").append(mHorizontalDpi);
   1206             builder.append(", verticalDpi: ").append(mVerticalDpi);
   1207             builder.append("}");
   1208             return builder.toString();
   1209         }
   1210     }
   1211 
   1212     /**
   1213      * This class specifies content margins. Margins define the white space
   1214      * around the content where the left margin defines the amount of white
   1215      * space on the left of the content and so on.
   1216      */
   1217     public static final class Margins {
   1218         public static final Margins NO_MARGINS = new Margins(0,  0,  0,  0);
   1219 
   1220         private final int mLeftMils;
   1221         private final int mTopMils;
   1222         private final int mRightMils;
   1223         private final int mBottomMils;
   1224 
   1225         /**
   1226          * Creates a new instance.
   1227          *
   1228          * @param leftMils The left margin in mils (thousandths of an inch).
   1229          * @param topMils The top margin in mils (thousandths of an inch).
   1230          * @param rightMils The right margin in mils (thousandths of an inch).
   1231          * @param bottomMils The bottom margin in mils (thousandths of an inch).
   1232          */
   1233         public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
   1234             mTopMils = topMils;
   1235             mLeftMils = leftMils;
   1236             mRightMils = rightMils;
   1237             mBottomMils = bottomMils;
   1238         }
   1239 
   1240         /**
   1241          * Gets the left margin in mils (thousandths of an inch).
   1242          *
   1243          * @return The left margin.
   1244          */
   1245         public int getLeftMils() {
   1246             return mLeftMils;
   1247         }
   1248 
   1249         /**
   1250          * Gets the top margin in mils (thousandths of an inch).
   1251          *
   1252          * @return The top margin.
   1253          */
   1254         public int getTopMils() {
   1255             return mTopMils;
   1256         }
   1257 
   1258         /**
   1259          * Gets the right margin in mils (thousandths of an inch).
   1260          *
   1261          * @return The right margin.
   1262          */
   1263         public int getRightMils() {
   1264             return mRightMils;
   1265         }
   1266 
   1267         /**
   1268          * Gets the bottom margin in mils (thousandths of an inch).
   1269          *
   1270          * @return The bottom margin.
   1271          */
   1272         public int getBottomMils() {
   1273             return mBottomMils;
   1274         }
   1275 
   1276         void writeToParcel(Parcel parcel) {
   1277             parcel.writeInt(mLeftMils);
   1278             parcel.writeInt(mTopMils);
   1279             parcel.writeInt(mRightMils);
   1280             parcel.writeInt(mBottomMils);
   1281         }
   1282 
   1283         static Margins createFromParcel(Parcel parcel) {
   1284             return new Margins(
   1285                     parcel.readInt(),
   1286                     parcel.readInt(),
   1287                     parcel.readInt(),
   1288                     parcel.readInt());
   1289         }
   1290 
   1291         @Override
   1292         public int hashCode() {
   1293             final int prime = 31;
   1294             int result = 1;
   1295             result = prime * result + mBottomMils;
   1296             result = prime * result + mLeftMils;
   1297             result = prime * result + mRightMils;
   1298             result = prime * result + mTopMils;
   1299             return result;
   1300         }
   1301 
   1302         @Override
   1303         public boolean equals(Object obj) {
   1304             if (this == obj) {
   1305                 return true;
   1306             }
   1307             if (obj == null) {
   1308                 return false;
   1309             }
   1310             if (getClass() != obj.getClass()) {
   1311                 return false;
   1312             }
   1313             Margins other = (Margins) obj;
   1314             if (mBottomMils != other.mBottomMils) {
   1315                 return false;
   1316             }
   1317             if (mLeftMils != other.mLeftMils) {
   1318                 return false;
   1319             }
   1320             if (mRightMils != other.mRightMils) {
   1321                 return false;
   1322             }
   1323             if (mTopMils != other.mTopMils) {
   1324                 return false;
   1325             }
   1326             return true;
   1327         }
   1328 
   1329         @Override
   1330         public String toString() {
   1331             StringBuilder builder = new StringBuilder();
   1332             builder.append("Margins{");
   1333             builder.append("leftMils: ").append(mLeftMils);
   1334             builder.append(", topMils: ").append(mTopMils);
   1335             builder.append(", rightMils: ").append(mRightMils);
   1336             builder.append(", bottomMils: ").append(mBottomMils);
   1337             builder.append("}");
   1338             return builder.toString();
   1339         }
   1340     }
   1341 
   1342     static String colorModeToString(int colorMode) {
   1343         switch (colorMode) {
   1344             case COLOR_MODE_MONOCHROME: {
   1345                 return "COLOR_MODE_MONOCHROME";
   1346             }
   1347             case COLOR_MODE_COLOR: {
   1348                 return "COLOR_MODE_COLOR";
   1349             }
   1350             default: {
   1351                 return "COLOR_MODE_UNKNOWN";
   1352             }
   1353         }
   1354     }
   1355 
   1356     static String duplexModeToString(int duplexMode) {
   1357         switch (duplexMode) {
   1358             case DUPLEX_MODE_NONE: {
   1359                 return "DUPLEX_MODE_NONE";
   1360             }
   1361             case DUPLEX_MODE_LONG_EDGE: {
   1362                 return "DUPLEX_MODE_LONG_EDGE";
   1363             }
   1364             case DUPLEX_MODE_SHORT_EDGE: {
   1365                 return "DUPLEX_MODE_SHORT_EDGE";
   1366             }
   1367             default: {
   1368                 return "DUPLEX_MODE_UNKNOWN";
   1369             }
   1370         }
   1371     }
   1372 
   1373     static void enforceValidColorMode(int colorMode) {
   1374         if ((colorMode & VALID_COLOR_MODES) == 0 || Integer.bitCount(colorMode) != 1) {
   1375             throw new IllegalArgumentException("invalid color mode: " + colorMode);
   1376         }
   1377     }
   1378 
   1379     static void enforceValidDuplexMode(int duplexMode) {
   1380         if ((duplexMode & VALID_DUPLEX_MODES) == 0 || Integer.bitCount(duplexMode) != 1) {
   1381             throw new IllegalArgumentException("invalid duplex mode: " + duplexMode);
   1382         }
   1383     }
   1384 
   1385     /**
   1386      * Builder for creating {@link PrintAttributes}.
   1387      */
   1388     public static final class Builder {
   1389         private final PrintAttributes mAttributes = new PrintAttributes();
   1390 
   1391         /**
   1392          * Sets the media size.
   1393          *
   1394          * @param mediaSize The media size.
   1395          * @return This builder.
   1396          */
   1397         public @NonNull Builder setMediaSize(@NonNull MediaSize mediaSize) {
   1398             mAttributes.setMediaSize(mediaSize);
   1399             return this;
   1400         }
   1401 
   1402         /**
   1403          * Sets the resolution.
   1404          *
   1405          * @param resolution The resolution.
   1406          * @return This builder.
   1407          */
   1408         public @NonNull Builder setResolution(@NonNull Resolution resolution) {
   1409             mAttributes.setResolution(resolution);
   1410             return this;
   1411         }
   1412 
   1413         /**
   1414          * Sets the minimal margins. If the content does not fit
   1415          * these margins it will be clipped.
   1416          *
   1417          * @param margins The margins.
   1418          * @return This builder.
   1419          */
   1420         public @NonNull Builder setMinMargins(@NonNull Margins margins) {
   1421             mAttributes.setMinMargins(margins);
   1422             return this;
   1423         }
   1424 
   1425         /**
   1426          * Sets the color mode.
   1427          *
   1428          * @param colorMode A valid color mode or zero.
   1429          * @return This builder.
   1430          *
   1431          * @see PrintAttributes#COLOR_MODE_MONOCHROME
   1432          * @see PrintAttributes#COLOR_MODE_COLOR
   1433          */
   1434         public @NonNull Builder setColorMode(@ColorMode int colorMode) {
   1435             mAttributes.setColorMode(colorMode);
   1436             return this;
   1437         }
   1438 
   1439         /**
   1440          * Sets the duplex mode.
   1441          *
   1442          * @param duplexMode A valid duplex mode or zero.
   1443          * @return This builder.
   1444          *
   1445          * @see PrintAttributes#DUPLEX_MODE_NONE
   1446          * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
   1447          * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
   1448          */
   1449         public @NonNull Builder setDuplexMode(@DuplexMode int duplexMode) {
   1450             mAttributes.setDuplexMode(duplexMode);
   1451             return this;
   1452         }
   1453 
   1454         /**
   1455          * Creates a new {@link PrintAttributes} instance.
   1456          *
   1457          * @return The new instance.
   1458          */
   1459         public @NonNull PrintAttributes build() {
   1460             return mAttributes;
   1461         }
   1462     }
   1463 
   1464     public static final Parcelable.Creator<PrintAttributes> CREATOR =
   1465             new Creator<PrintAttributes>() {
   1466         @Override
   1467         public PrintAttributes createFromParcel(Parcel parcel) {
   1468             return new PrintAttributes(parcel);
   1469         }
   1470 
   1471         @Override
   1472         public PrintAttributes[] newArray(int size) {
   1473             return new PrintAttributes[size];
   1474         }
   1475     };
   1476 }
   1477