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