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