PrintAttributes.java revision 651dd4e6ee6510caf9f15c51094a11121af17ec2
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.Log;
26
27import com.android.internal.R;
28
29/**
30 * This class represents the attributes of a print job.
31 */
32public final class PrintAttributes implements Parcelable {
33
34    /** Color mode: Monochrome color scheme, e.g. one color is used. */
35    public static final int COLOR_MODE_MONOCHROME = 1 << 0;
36    /** Color mode: Color color scheme, e.g. many colors are used. */
37    public static final int COLOR_MODE_COLOR = 1 << 1;
38
39    private static final int VALID_COLOR_MODES =
40            COLOR_MODE_MONOCHROME | COLOR_MODE_COLOR;
41
42    private MediaSize mMediaSize;
43    private Resolution mResolution;
44    private Margins mMinMargins;
45
46    private int mColorMode;
47
48    PrintAttributes() {
49        /* hide constructor */
50    }
51
52    private PrintAttributes(Parcel parcel) {
53        mMediaSize = (parcel.readInt() ==  1) ? MediaSize.createFromParcel(parcel) : null;
54        mResolution = (parcel.readInt() ==  1) ? Resolution.createFromParcel(parcel) : null;
55        mMinMargins = (parcel.readInt() ==  1) ? Margins.createFromParcel(parcel) : null;
56        mColorMode = parcel.readInt();
57    }
58
59    /**
60     * Gets the media size.
61     *
62     * @return The media size or <code>null</code> if not set.
63     */
64    public MediaSize getMediaSize() {
65        return mMediaSize;
66    }
67
68    /**
69     * Sets the media size.
70     *
71     * @param The media size.
72     *
73     * @hide
74     */
75    public void setMediaSize(MediaSize mediaSize) {
76        mMediaSize = mediaSize;
77    }
78
79    /**
80     * Gets the resolution.
81     *
82     * @return The resolution or <code>null</code> if not set.
83     */
84    public Resolution getResolution() {
85        return mResolution;
86    }
87
88    /**
89     * Sets the resolution.
90     *
91     * @param The resolution.
92     *
93     * @hide
94     */
95    public void setResolution(Resolution resolution) {
96        mResolution = resolution;
97    }
98
99    /**
100     * Gets the minimal margins. If the content does not fit
101     * these margins it will be clipped.
102     *
103     * @return The margins or <code>null</code> if not set.
104     */
105    public Margins getMinMargins() {
106        return mMinMargins;
107    }
108
109    /**
110     * Sets the minimal margins. If the content does not fit
111     * these margins it will be clipped.
112     *
113     * @param The margins.
114     *
115     * @hide
116     */
117    public void setMinMargins(Margins margins) {
118        mMinMargins = margins;
119    }
120
121    /**
122     * Gets the color mode.
123     *
124     * @return The color mode or zero if not set.
125     *
126     * @see #COLOR_MODE_COLOR
127     * @see #COLOR_MODE_MONOCHROME
128     */
129    public int getColorMode() {
130        return mColorMode;
131    }
132
133    /**
134     * Sets the color mode.
135     *
136     * @param The color mode.
137     *
138     * @see #COLOR_MODE_MONOCHROME
139     * @see #COLOR_MODE_COLOR
140     *
141     * @hide
142     */
143    public void setColorMode(int colorMode) {
144        enforceValidColorMode(colorMode);
145        mColorMode = colorMode;
146    }
147
148    @Override
149    public void writeToParcel(Parcel parcel, int flags) {
150        if (mMediaSize != null) {
151            parcel.writeInt(1);
152            mMediaSize.writeToParcel(parcel);
153        } else {
154            parcel.writeInt(0);
155        }
156        if (mResolution != null) {
157            parcel.writeInt(1);
158            mResolution.writeToParcel(parcel);
159        } else {
160            parcel.writeInt(0);
161        }
162        if (mMinMargins != null) {
163            parcel.writeInt(1);
164            mMinMargins.writeToParcel(parcel);
165        } else {
166            parcel.writeInt(0);
167        }
168        parcel.writeInt(mColorMode);
169    }
170
171    @Override
172    public int describeContents() {
173        return 0;
174    }
175
176    @Override
177    public int hashCode() {
178        final int prime = 31;
179        int result = 1;
180        result = prime * result + mColorMode;
181        result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
182        result = prime * result + ((mMediaSize == null) ? 0 : mMediaSize.hashCode());
183        result = prime * result + ((mResolution == null) ? 0 : mResolution.hashCode());
184        return result;
185    }
186
187    @Override
188    public boolean equals(Object obj) {
189        if (this == obj) {
190            return true;
191        }
192        if (obj == null) {
193            return false;
194        }
195        if (getClass() != obj.getClass()) {
196            return false;
197        }
198        PrintAttributes other = (PrintAttributes) obj;
199        if (mColorMode != other.mColorMode) {
200            return false;
201        }
202        if (mMinMargins == null) {
203            if (other.mMinMargins != null) {
204                return false;
205            }
206        } else if (!mMinMargins.equals(other.mMinMargins)) {
207            return false;
208        }
209        if (mMediaSize == null) {
210            if (other.mMediaSize != null) {
211                return false;
212            }
213        } else if (!mMediaSize.equals(other.mMediaSize)) {
214            return false;
215        }
216        if (mResolution == null) {
217            if (other.mResolution != null) {
218                return false;
219            }
220        } else if (!mResolution.equals(other.mResolution)) {
221            return false;
222        }
223        return true;
224    }
225
226    @Override
227    public String toString() {
228        StringBuilder builder = new StringBuilder();
229        builder.append("PrintAttributes{");
230        builder.append("mediaSize: ").append(mMediaSize);
231        if (mMediaSize != null) {
232            builder.append(", orientation: ").append(mMediaSize.isPortrait()
233                    ? "portrait" : "landscape");
234        } else {
235            builder.append(", orientation: ").append("null");
236        }
237        builder.append(", resolution: ").append(mResolution);
238        builder.append(", minMargins: ").append(mMinMargins);
239        builder.append(", colorMode: ").append(colorModeToString(mColorMode));
240        builder.append("}");
241        return builder.toString();
242    }
243
244    /** hide */
245    public void clear() {
246        mMediaSize = null;
247        mResolution = null;
248        mMinMargins = null;
249        mColorMode = 0;
250    }
251
252    /**
253     * @hide
254     */
255    public void copyFrom(PrintAttributes other) {
256        mMediaSize = other.mMediaSize;
257        mResolution = other.mResolution;
258        mMinMargins = other.mMinMargins;
259        mColorMode = other.mColorMode;
260    }
261
262    /**
263     * This class specifies a supported media size.
264     */
265    public static final class MediaSize {
266        private static final String LOG_TAG = "MediaSize";
267
268        // TODO: Verify media sizes and add more standard ones.
269
270        // ISO sizes
271
272        /** ISO A0 media size: 841mm x 1189mm (33.11" x 46.81") */
273        public static final MediaSize ISO_A0 =
274                new MediaSize("ISO_A0", "android", R.string.mediaSize_iso_a0, 33110, 46810);
275        /** ISO A1 media size: 594mm x 841mm (23.39" x 33.11") */
276        public static final MediaSize ISO_A1 =
277                new MediaSize("ISO_A1", "android", R.string.mediaSize_iso_a1, 23390, 33110);
278        /** ISO A2 media size: 420mm x 594mm (16.54" x 23.39") */
279        public static final MediaSize ISO_A2 =
280                new MediaSize("ISO_A2", "android", R.string.mediaSize_iso_a2, 16540, 23390);
281        /** ISO A3 media size: 297mm x 420mm (11.69" x 16.54") */
282        public static final MediaSize ISO_A3 =
283                new MediaSize("ISO_A3", "android", R.string.mediaSize_iso_a3, 11690, 16540);
284        /** ISO A4 media size: 210mm x 297mm (8.27" x 11.69") */
285        public static final MediaSize ISO_A4 =
286                new MediaSize("ISO_A4", "android", R.string.mediaSize_iso_a4, 8270, 11690);
287        /** ISO A5 media size: 148mm x 210mm (5.83" x 8.27") */
288        public static final MediaSize ISO_A5 =
289                new MediaSize("ISO_A5", "android", R.string.mediaSize_iso_a5, 5830, 8270);
290        /** ISO A6 media size: 105mm x 148mm (4.13" x 5.83") */
291        public static final MediaSize ISO_A6 =
292                new MediaSize("ISO_A6", "android", R.string.mediaSize_iso_a6, 4130, 5830);
293        /** ISO A7 media size: 74mm x 105mm (2.91" x 4.13") */
294        public static final MediaSize ISO_A7 =
295                new MediaSize("ISO_A7", "android", R.string.mediaSize_iso_a7, 2910, 4130);
296        /** ISO A8 media size: 52mm x 74mm (2.05" x 2.91") */
297        public static final MediaSize ISO_A8 =
298                new MediaSize("ISO_A8", "android", R.string.mediaSize_iso_a8, 2050, 2910);
299        /** ISO A9 media size: 37mm x 52mm (1.46" x 2.05") */
300        public static final MediaSize ISO_A9 =
301                new MediaSize("ISO_A9", "android", R.string.mediaSize_iso_a9, 1460, 2050);
302        /** ISO A10 media size: 26mm x 37mm (1.02" x 1.46") */
303        public static final MediaSize ISO_A10 =
304                new MediaSize("ISO_A10", "android", R.string.mediaSize_iso_a10, 1020, 1460);
305
306        /** ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67") */
307        public static final MediaSize ISO_B0 =
308                new MediaSize("ISO_B0", "android", R.string.mediaSize_iso_b0, 39370, 55670);
309        /** ISO B1 media size: 707mm x 1000mm (27.83" x 39.37") */
310        public static final MediaSize ISO_B1 =
311                new MediaSize("ISO_B1", "android", R.string.mediaSize_iso_b1, 27830, 39370);
312        /** ISO B2 media size: 500mm x 707mm (19.69" x 27.83") */
313        public static final MediaSize ISO_B2 =
314                new MediaSize("ISO_B2", "android", R.string.mediaSize_iso_b2, 19690, 27830);
315        /** ISO B3 media size: 353mm x 500mm (13.90" x 19.69") */
316        public static final MediaSize ISO_B3 =
317                new MediaSize("ISO_B3", "android", R.string.mediaSize_iso_b3, 13900, 19690);
318        /** ISO B4 media size: 250mm x 353mm (9.84" x 13.90") */
319        public static final MediaSize ISO_B4 =
320                new MediaSize("ISO_B4", "android", R.string.mediaSize_iso_b4, 9840, 13900);
321        /** ISO B5 media size: 176mm x 250mm (6.93" x 9.84") */
322        public static final MediaSize ISO_B5 =
323                new MediaSize("ISO_B5", "android", R.string.mediaSize_iso_b5, 6930, 9840);
324        /** ISO B6 media size: 125mm x 176mm (4.92" x 6.93") */
325        public static final MediaSize ISO_B6 =
326                new MediaSize("ISO_B6", "android", R.string.mediaSize_iso_b6, 4920, 6930);
327        /** ISO B7 media size: 88mm x 125mm (3.46" x 4.92") */
328        public static final MediaSize ISO_B7 =
329                new MediaSize("ISO_B7", "android", R.string.mediaSize_iso_b7, 3460, 4920);
330        /** ISO B8 media size: 62mm x 88mm (2.44" x 3.46") */
331        public static final MediaSize ISO_B8 =
332                new MediaSize("ISO_B8", "android", R.string.mediaSize_iso_b8, 2440, 3460);
333        /** ISO B9 media size: 44mm x 62mm (1.73" x 2.44") */
334        public static final MediaSize ISO_B9 =
335                new MediaSize("ISO_B9", "android", R.string.mediaSize_iso_b9, 1730, 2440);
336        /** ISO B10 media size: 31mm x 44mm (1.22" x 1.73") */
337        public static final MediaSize ISO_B10 =
338                new MediaSize("ISO_B10", "android", R.string.mediaSize_iso_b10, 1220, 1730);
339
340        /** ISO C0 media size: 917mm x 1297mm (36.10" x 51.06") */
341        public static final MediaSize ISO_C0 =
342                new MediaSize("ISO_C0", "android", R.string.mediaSize_iso_c0, 36100, 51060);
343        /** ISO C1 media size: 648mm x 917mm (25.51" x 36.10") */
344        public static final MediaSize ISO_C1 =
345                new MediaSize("ISO_C1", "android", R.string.mediaSize_iso_c1, 25510, 36100);
346        /** ISO C2 media size: 458mm x 648mm (18.03" x 25.51") */
347        public static final MediaSize ISO_C2 =
348                new MediaSize("ISO_C2", "android", R.string.mediaSize_iso_c2, 18030, 25510);
349        /** ISO C3 media size: 324mm x 458mm (12.76" x 18.03") */
350        public static final MediaSize ISO_C3 =
351                new MediaSize("ISO_C3", "android", R.string.mediaSize_iso_c3, 12760, 18030);
352        /** ISO C4 media size: 229mm x 324mm (9.02" x 12.76") */
353        public static final MediaSize ISO_C4 =
354                new MediaSize("ISO_C4", "android", R.string.mediaSize_iso_c4, 9020, 12760);
355        /** ISO C5 media size: 162mm x 229mm (6.38" x 9.02") */
356        public static final MediaSize ISO_C5 =
357                new MediaSize("ISO_C5", "android", R.string.mediaSize_iso_c5, 6380, 9020);
358        /** ISO C6 media size: 114mm x 162mm (4.49" x 6.38") */
359        public static final MediaSize ISO_C6 =
360                new MediaSize("ISO_C6", "android", R.string.mediaSize_iso_c6, 4490, 6380);
361        /** ISO C7 media size: 81mm x 114mm (3.19" x 4.49") */
362        public static final MediaSize ISO_C7 =
363                new MediaSize("ISO_C7", "android", R.string.mediaSize_iso_c7, 3190, 4490);
364        /** ISO C8 media size: 57mm x 81mm (2.24" x 3.19") */
365        public static final MediaSize ISO_C8 =
366                new MediaSize("ISO_C8", "android", R.string.mediaSize_iso_c8, 2240, 3190);
367        /** ISO C9 media size: 40mm x 57mm (1.57" x 2.24") */
368        public static final MediaSize ISO_C9 =
369                new MediaSize("ISO_C9", "android", R.string.mediaSize_iso_c9, 1570, 2240);
370        /** ISO C10 media size: 28mm x 40mm (1.10" x 1.57") */
371        public static final MediaSize ISO_C10 =
372                new MediaSize("ISO_C10", "android", R.string.mediaSize_iso_c10, 1100, 1570);
373
374        // North America
375
376        /** North America Letter media size: 8.5" x 11" */
377        public static final MediaSize NA_LETTER =
378                new MediaSize("NA_LETTER", "android", R.string.mediaSize_na_letter, 8500, 11000);
379        /** North America Government-Letter media size: 8.0" x 10.5" */
380        public static final MediaSize NA_GOVT_LETTER =
381                new MediaSize("NA_GOVT_LETTER", "android",
382                        R.string.mediaSize_na_gvrnmt_letter, 8000, 10500);
383        /** North America Legal media size: 8.5" x 14" */
384        public static final MediaSize NA_LEGAL =
385                new MediaSize("NA_LEGAL", "android", R.string.mediaSize_na_legal, 8500, 14000);
386        /** North America Junior Legal media size: 8.0" x 5.0" */
387        public static final MediaSize NA_JUNIOR_LEGAL =
388                new MediaSize("NA_JUNIOR_LEGAL", "android",
389                        R.string.mediaSize_na_junior_legal, 8000, 5000);
390        /** North America Ledger media size: 17" x 11" */
391        public static final MediaSize NA_LEDGER =
392                new MediaSize("NA_LEDGER", "android", R.string.mediaSize_na_ledger, 17000, 11000);
393        /** North America Tabloid media size: 11" x 17" */
394        public static final MediaSize NA_TBLOID =
395                new MediaSize("NA_TABLOID", "android",
396                        R.string.mediaSize_na_tabloid, 11000, 17000);
397
398        private final String mId;
399        /**@hide */
400        public final String mLabel;
401        /**@hide */
402        public final String mPackageName;
403        /**@hide */
404        public final int mLabelResId;
405        private final int mWidthMils;
406        private final int mHeightMils;
407
408        /**
409         * Creates a new instance. This is the preferred constructor since
410         * it enables the media size label to be shown in a localized fashion
411         * on a locale change.
412         *
413         * @param id The unique media size id.
414         * @param packageName The name of the creating package.
415         * @param labelResId The resource if of a human readable label.
416         * @param widthMils The width in mils (thousands of an inch).
417         * @param heightMils The height in mils (thousands of an inch).
418         *
419         * @throws IllegalArgumentException If the id is empty.
420         * @throws IllegalArgumentException If the label is empty.
421         * @throws IllegalArgumentException If the widthMils is less than or equal to zero.
422         * @throws IllegalArgumentException If the heightMils is less than or equal to zero.
423         *
424         * @hide
425         */
426        public MediaSize(String id, String packageName, int labelResId,
427                int widthMils, int heightMils) {
428            if (TextUtils.isEmpty(id)) {
429                throw new IllegalArgumentException("id cannot be empty.");
430            }
431            if (TextUtils.isEmpty(packageName)) {
432                throw new IllegalArgumentException("packageName cannot be empty.");
433            }
434            if (labelResId <= 0) {
435                throw new IllegalArgumentException("labelResId must be greater than zero.");
436            }
437            if (widthMils <= 0) {
438                throw new IllegalArgumentException("widthMils "
439                        + "cannot be less than or equal to zero.");
440            }
441            if (heightMils <= 0) {
442                throw new IllegalArgumentException("heightMils "
443                       + "cannot be less than or euqual to zero.");
444            }
445            mPackageName = packageName;
446            mId = id;
447            mLabelResId = labelResId;
448            mWidthMils = widthMils;
449            mHeightMils = heightMils;
450            mLabel = null;
451        }
452
453        /**
454         * Creates a new instance.
455         *
456         * @param id The unique media size id.
457         * @param label The <strong>internationalized</strong> human readable label.
458         * @param widthMils The width in mils (thousands of an inch).
459         * @param heightMils The height in mils (thousands of an inch).
460         *
461         * @throws IllegalArgumentException If the id is empty.
462         * @throws IllegalArgumentException If the label is empty.
463         * @throws IllegalArgumentException If the widthMils is less than or equal to zero.
464         * @throws IllegalArgumentException If the heightMils is less than or equal to zero.
465         */
466        public MediaSize(String id, String label, int widthMils, int heightMils) {
467            if (TextUtils.isEmpty(id)) {
468                throw new IllegalArgumentException("id cannot be empty.");
469            }
470            if (TextUtils.isEmpty(label)) {
471                throw new IllegalArgumentException("label cannot be empty.");
472            }
473            if (widthMils <= 0) {
474                throw new IllegalArgumentException("widthMils "
475                        + "cannot be less than or equal to zero.");
476            }
477            if (heightMils <= 0) {
478                throw new IllegalArgumentException("heightMils "
479                       + "cannot be less than or euqual to zero.");
480            }
481            mId = id;
482            mLabel = label;
483            mWidthMils = widthMils;
484            mHeightMils = heightMils;
485            mLabelResId = 0;
486            mPackageName = null;
487        }
488
489        /** @hide */
490        public MediaSize(String id, String label, String packageName,
491                int widthMils, int heightMils, int labelResId) {
492            mPackageName = packageName;
493            mId = id;
494            mLabelResId = labelResId;
495            mWidthMils = widthMils;
496            mHeightMils = heightMils;
497            mLabel = label;
498        }
499
500        /**
501         * Gets the unique media size id.
502         *
503         * @return The unique media size id.
504         */
505        public String getId() {
506            return mId;
507        }
508
509        /**
510         * Gets the human readable media size label.
511         *
512         * @param packageManager The package manager for loading the label.
513         * @return The human readable label.
514         */
515        public String getLabel(PackageManager packageManager) {
516            if (!TextUtils.isEmpty(mPackageName) && mLabelResId > 0) {
517                try {
518                    return packageManager.getResourcesForApplication(
519                            mPackageName).getString(mLabelResId);
520                } catch (NotFoundException nfe) {
521                    Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
522                            + " from package " + mPackageName);
523                } catch (NameNotFoundException nnfee) {
524                    Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
525                            + " from package " + mPackageName);
526                }
527            }
528            return mLabel;
529        }
530
531        /**
532         * Gets the media width in mils (thousands of an inch).
533         *
534         * @return The media width.
535         */
536        public int getWidthMils() {
537            return mWidthMils;
538        }
539
540        /**
541         * Gets the media height in mils (thousands of an inch).
542         *
543         * @return The media height.
544         */
545        public int getHeightMils() {
546            return mHeightMils;
547        }
548
549        /**
550         * Gets whether this media size is in portrait which is the
551         * height is greater or equal to the width.
552         *
553         * @return True if the media size is in portrait, false if
554         * it is in landscape.
555         */
556        public boolean isPortrait() {
557            return mHeightMils >= mWidthMils;
558        }
559
560        /**
561         * Returns a new media size in a portrait orientation
562         * which is the height is the greater dimension.
563         *
564         * @return New instance in landscape orientation.
565         */
566        public MediaSize asPortrait() {
567            return new MediaSize(mId, mLabel, mPackageName,
568                    Math.min(mWidthMils, mHeightMils),
569                    Math.max(mWidthMils, mHeightMils),
570                    mLabelResId);
571        }
572
573        /**
574         * Returns a new media size in a landscape orientation
575         * which is the height is the lesser dimension.
576         *
577         * @return New instance in landscape orientation.
578         */
579        public MediaSize asLandscape() {
580            return new MediaSize(mId, mLabel, mPackageName,
581                    Math.max(mWidthMils, mHeightMils),
582                    Math.min(mWidthMils, mHeightMils),
583                    mLabelResId);
584        }
585
586        void writeToParcel(Parcel parcel) {
587            parcel.writeString(mId);
588            parcel.writeString(mLabel);
589            parcel.writeString(mPackageName);
590            parcel.writeInt(mWidthMils);
591            parcel.writeInt(mHeightMils);
592            parcel.writeInt(mLabelResId);
593        }
594
595        static MediaSize createFromParcel(Parcel parcel) {
596            return new MediaSize(
597                    parcel.readString(),
598                    parcel.readString(),
599                    parcel.readString(),
600                    parcel.readInt(),
601                    parcel.readInt(),
602                    parcel.readInt());
603        }
604
605        @Override
606        public int hashCode() {
607            final int prime = 31;
608            int result = 1;
609            result = prime * result + mWidthMils;
610            result = prime * result + mHeightMils;
611            return result;
612        }
613
614        @Override
615        public boolean equals(Object obj) {
616            if (this == obj) {
617                return true;
618            }
619            if (obj == null) {
620                return false;
621            }
622            if (getClass() != obj.getClass()) {
623                return false;
624            }
625            MediaSize other = (MediaSize) obj;
626            if (mWidthMils != other.mWidthMils) {
627                return false;
628            }
629            if (mHeightMils != other.mHeightMils) {
630                return false;
631            }
632            return true;
633        }
634
635        @Override
636        public String toString() {
637            StringBuilder builder = new StringBuilder();
638            builder.append("MediaSize{");
639            builder.append("id: ").append(mId);
640            builder.append(", label: ").append(mLabel);
641            builder.append(", packageName: ").append(mPackageName);
642            builder.append(", heightMils: ").append(mHeightMils);
643            builder.append(", widthMils: ").append(mWidthMils);
644            builder.append(", labelResId: ").append(mLabelResId);
645            builder.append("}");
646            return builder.toString();
647        }
648    }
649
650    /**
651     * This class specifies a supported resolution in dpi (dots per inch).
652     */
653    public static final class Resolution {
654        private final String mId;
655        private final String mLabel;
656        private final int mHorizontalDpi;
657        private final int mVerticalDpi;
658
659        /**
660         * Creates a new instance.
661         *
662         * @param id The unique resolution id.
663         * @param label The <strong>internationalized</strong> human readable label.
664         * @param horizontalDpi The horizontal resolution in dpi.
665         * @param verticalDpi The vertical resolution in dpi.
666         *
667         * @throws IllegalArgumentException If the id is empty.
668         * @throws IllegalArgumentException If the label is empty.
669         * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero.
670         * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero.
671         */
672        public Resolution(String id, String label, int horizontalDpi, int verticalDpi) {
673            if (TextUtils.isEmpty(id)) {
674                throw new IllegalArgumentException("id cannot be empty.");
675            }
676            if (TextUtils.isEmpty(label)) {
677                throw new IllegalArgumentException("label cannot be empty.");
678            }
679            if (horizontalDpi <= 0) {
680                throw new IllegalArgumentException("horizontalDpi "
681                        + "cannot be less than or equal to zero.");
682            }
683            if (verticalDpi <= 0) {
684                throw new IllegalArgumentException("verticalDpi"
685                       + " cannot be less than or equal to zero.");
686            }
687            mId = id;
688            mLabel = label;
689            mHorizontalDpi = horizontalDpi;
690            mVerticalDpi = verticalDpi;
691        }
692
693        /**
694         * Gets the unique resolution id.
695         *
696         * @return The unique resolution id.
697         */
698        public String getId() {
699            return mId;
700        }
701
702        /**
703         * Gets the resolution human readable label.
704         *
705         * @return The human readable label.
706         */
707        public String getLabel() {
708            return mLabel;
709        }
710
711        /**
712         * Gets the vertical resolution in dpi.
713         *
714         * @return The horizontal resolution.
715         */
716        public int getHorizontalDpi() {
717            return mHorizontalDpi;
718        }
719
720        /**
721         * Gets the vertical resolution in dpi.
722         *
723         * @return The vertical resolution.
724         */
725        public int getVerticalDpi() {
726            return mVerticalDpi;
727        }
728
729        void writeToParcel(Parcel parcel) {
730            parcel.writeString(mId);
731            parcel.writeString(mLabel);
732            parcel.writeInt(mHorizontalDpi);
733            parcel.writeInt(mVerticalDpi);
734        }
735
736        static Resolution createFromParcel(Parcel parcel) {
737            return new Resolution(
738                    parcel.readString(),
739                    parcel.readString(),
740                    parcel.readInt(),
741                    parcel.readInt());
742        }
743
744        @Override
745        public int hashCode() {
746            final int prime = 31;
747            int result = 1;
748            result = prime * result + mHorizontalDpi;
749            result = prime * result + mVerticalDpi;
750            return result;
751        }
752
753        @Override
754        public boolean equals(Object obj) {
755            if (this == obj) {
756                return true;
757            }
758            if (obj == null) {
759                return false;
760            }
761            if (getClass() != obj.getClass()) {
762                return false;
763            }
764            Resolution other = (Resolution) obj;
765            if (mHorizontalDpi != other.mHorizontalDpi) {
766                return false;
767            }
768            if (mVerticalDpi != other.mVerticalDpi) {
769                return false;
770            }
771            return true;
772        }
773
774        @Override
775        public String toString() {
776            StringBuilder builder = new StringBuilder();
777            builder.append("Resolution{");
778            builder.append("id: ").append(mId);
779            builder.append(", label: ").append(mLabel);
780            builder.append(", horizontalDpi: ").append(mHorizontalDpi);
781            builder.append(", verticalDpi: ").append(mVerticalDpi);
782            builder.append("}");
783            return builder.toString();
784        }
785    }
786
787    /**
788     * This class specifies content margins.
789     */
790    public static final class Margins {
791        public static final Margins NO_MARGINS = new Margins(0,  0,  0,  0);
792
793        private final int mLeftMils;
794        private final int mTopMils;
795        private final int mRightMils;
796        private final int mBottomMils;
797
798        /**
799         * Creates a new instance.
800         *
801         * @param leftMils The left margin in mils (thousands of an inch).
802         * @param topMils The top margin in mils (thousands of an inch).
803         * @param rightMils The right margin in mils (thousands of an inch).
804         * @param bottomMils The bottom margin in mils (thousands of an inch).
805         */
806        public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
807            mTopMils = topMils;
808            mLeftMils = leftMils;
809            mRightMils = rightMils;
810            mBottomMils = bottomMils;
811        }
812
813        /**
814         * Gets the left margin in mils (thousands of an inch).
815         *
816         * @return The left margin.
817         */
818        public int getLeftMils() {
819            return mLeftMils;
820        }
821
822        /**
823         * Gets the top margin in mils (thousands of an inch).
824         *
825         * @return The top margin.
826         */
827        public int getTopMils() {
828            return mTopMils;
829        }
830
831        /**
832         * Gets the right margin in mils (thousands of an inch).
833         *
834         * @return The right margin.
835         */
836        public int getRightMils() {
837            return mRightMils;
838        }
839
840        /**
841         * Gets the bottom margin in mils (thousands of an inch).
842         *
843         * @return The bottom margin.
844         */
845        public int getBottomMils() {
846            return mBottomMils;
847        }
848
849        void writeToParcel(Parcel parcel) {
850            parcel.writeInt(mLeftMils);
851            parcel.writeInt(mTopMils);
852            parcel.writeInt(mRightMils);
853            parcel.writeInt(mBottomMils);
854        }
855
856        static Margins createFromParcel(Parcel parcel) {
857            return new Margins(
858                    parcel.readInt(),
859                    parcel.readInt(),
860                    parcel.readInt(),
861                    parcel.readInt());
862        }
863
864        @Override
865        public int hashCode() {
866            final int prime = 31;
867            int result = 1;
868            result = prime * result + mBottomMils;
869            result = prime * result + mLeftMils;
870            result = prime * result + mRightMils;
871            result = prime * result + mTopMils;
872            return result;
873        }
874
875        @Override
876        public boolean equals(Object obj) {
877            if (this == obj) {
878                return true;
879            }
880            if (obj == null) {
881                return false;
882            }
883            if (getClass() != obj.getClass()) {
884                return false;
885            }
886            Margins other = (Margins) obj;
887            if (mBottomMils != other.mBottomMils) {
888                return false;
889            }
890            if (mLeftMils != other.mLeftMils) {
891                return false;
892            }
893            if (mRightMils != other.mRightMils) {
894                return false;
895            }
896            if (mTopMils != other.mTopMils) {
897                return false;
898            }
899            return true;
900        }
901
902        @Override
903        public String toString() {
904            StringBuilder builder = new StringBuilder();
905            builder.append("Margins{");
906            builder.append("leftMils: ").append(mLeftMils);
907            builder.append(", topMils: ").append(mTopMils);
908            builder.append(", rightMils: ").append(mRightMils);
909            builder.append(", bottomMils: ").append(mBottomMils);
910            builder.append("}");
911            return builder.toString();
912        }
913    }
914
915    static String colorModeToString(int colorMode) {
916        switch (colorMode) {
917            case COLOR_MODE_MONOCHROME: {
918                return "COLOR_MODE_MONOCHROME";
919            }
920            case COLOR_MODE_COLOR: {
921                return "COLOR_MODE_COLOR";
922            }
923            default:
924                return "COLOR_MODE_UNKNOWN";
925        }
926    }
927
928    static void enforceValidColorMode(int colorMode) {
929        if ((colorMode & VALID_COLOR_MODES) == 0 && Integer.bitCount(colorMode) == 1) {
930            throw new IllegalArgumentException("invalid color mode: " + colorMode);
931        }
932    }
933
934    /**
935     * Builder for creating {@link PrintAttributes}.
936     */
937    public static final class Builder {
938        private final PrintAttributes mAttributes = new PrintAttributes();
939
940        /**
941         * Sets the media size.
942         *
943         * @param mediaSize The media size.
944         * @return This builder.
945         */
946        public Builder setMediaSize(MediaSize mediaSize) {
947            mAttributes.setMediaSize(mediaSize);
948            return this;
949        }
950
951        /**
952         * Sets the resolution.
953         *
954         * @param resolution The resolution.
955         * @return This builder.
956         */
957        public Builder setResolution(Resolution resolution) {
958            mAttributes.setResolution(resolution);
959            return this;
960        }
961
962        /**
963         * Sets the minimal margins. If the content does not fit
964         * these margins it will be clipped.
965         *
966         * @param margins The margins.
967         * @return This builder.
968         */
969        public Builder setMinMargins(Margins margins) {
970            mAttributes.setMinMargins(margins);
971            return this;
972        }
973
974        /**
975         * Sets the color mode.
976         *
977         * @param colorMode A valid color mode or zero.
978         * @return This builder.
979         *
980         * @see PrintAttributes#COLOR_MODE_MONOCHROME
981         * @see PrintAttributes#COLOR_MODE_COLOR
982         */
983        public Builder setColorMode(int colorMode) {
984            if (Integer.bitCount(colorMode) > 1) {
985                throw new IllegalArgumentException("can specify at most one colorMode bit.");
986            }
987            mAttributes.setColorMode(colorMode);
988            return this;
989        }
990
991        /**
992         * Creates a new {@link PrintAttributes} instance.
993         *
994         * @return The new instance.
995         */
996        public PrintAttributes build() {
997            return mAttributes;
998        }
999    }
1000
1001    public static final Parcelable.Creator<PrintAttributes> CREATOR =
1002            new Creator<PrintAttributes>() {
1003        @Override
1004        public PrintAttributes createFromParcel(Parcel parcel) {
1005            return new PrintAttributes(parcel);
1006        }
1007
1008        @Override
1009        public PrintAttributes[] newArray(int size) {
1010            return new PrintAttributes[size];
1011        }
1012    };
1013}
1014