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