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