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