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