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