PrinterInfo.java revision a00271533f639c8ed36429c663889ac9f654bc72
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.print;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.print.PrintAttributes.Margins;
22import android.print.PrintAttributes.MediaSize;
23import android.print.PrintAttributes.Resolution;
24import android.print.PrintAttributes.Tray;
25import android.text.TextUtils;
26import android.util.SparseIntArray;
27
28import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * This class represents the description of a printer. A description
33 * contains the printer id, human readable name, status, and available
34 * options for various printer capabilities, such as media size, etc.
35 */
36public final class PrinterInfo implements Parcelable {
37    /**
38     * Undefined default value.
39     *
40     * @hide
41     */
42    public static final int DEFAULT_UNDEFINED = -1;
43
44    private static final int MIN_COPIES = 1;
45
46    private static final int PROPERTY_MEDIA_SIZE = 0;
47    private static final int PROPERTY_RESOLUTION = 1;
48    private static final int PROPERTY_INPUT_TRAY = 2;
49    private static final int PROPERTY_OUTPUT_TRAY = 3;
50    private static final int PROPERTY_DUPLEX_MODE = 4;
51    private static final int PROPERTY_COLOR_MODE = 5;
52    private static final int PROPERTY_FITTING_MODE = 6;
53    private static final int PROPERTY_ORIENTATION = 7;
54
55    /** Printer status: the printer is ready to print. */
56    public static final int STATUS_READY = 1;
57
58    // TODO: Add printer status constants.
59
60    private PrinterId mId;
61    private CharSequence mLabel;
62    private int mStatus;
63
64    private Margins mMinMargins;
65    private final List<MediaSize> mMediaSizes = new ArrayList<MediaSize>(); // required
66    private final List<Resolution> mResolutions = new ArrayList<Resolution>(); // required
67    private List<Tray> mInputTrays;
68    private List<Tray> mOutputTrays;
69
70    private int mDuplexModes;
71    private int mColorModes;
72    private int mFittingModes;
73    private int mOrientations;
74
75    private final SparseIntArray mDefaults = new SparseIntArray();
76    private Margins mDefaultMargins;
77
78    private PrinterInfo() {
79        mDefaults.put(PROPERTY_MEDIA_SIZE, DEFAULT_UNDEFINED);
80        mDefaults.put(PROPERTY_RESOLUTION, DEFAULT_UNDEFINED);
81        mDefaults.put(PROPERTY_INPUT_TRAY, DEFAULT_UNDEFINED);
82        mDefaults.put(PROPERTY_OUTPUT_TRAY, DEFAULT_UNDEFINED);
83        mDefaults.put(PROPERTY_DUPLEX_MODE, DEFAULT_UNDEFINED);
84        mDefaults.put(PROPERTY_COLOR_MODE, DEFAULT_UNDEFINED);
85        mDefaults.put(PROPERTY_FITTING_MODE, DEFAULT_UNDEFINED);
86        mDefaults.put(PROPERTY_ORIENTATION, DEFAULT_UNDEFINED);
87    }
88
89    private PrinterInfo(PrinterInfo prototype) {
90        mId = prototype.mId;
91        mLabel = prototype.mLabel;
92        mStatus = prototype.mStatus;
93
94        mMinMargins = prototype.mMinMargins;
95        mMediaSizes.addAll(prototype.mMediaSizes);
96        mResolutions.addAll(prototype.mResolutions);
97        mInputTrays = (prototype.mInputTrays != null)
98                ? new ArrayList<Tray>(prototype.mInputTrays) : null;
99        mOutputTrays = (prototype.mOutputTrays != null)
100                ? new ArrayList<Tray>(prototype.mOutputTrays) : null;
101
102        mDuplexModes = prototype.mDuplexModes;
103        mColorModes = prototype.mColorModes;
104        mFittingModes = prototype.mFittingModes;
105        mOrientations = prototype.mOrientations;
106
107        final int defaultCount = prototype.mDefaults.size();
108        for (int i = 0; i < defaultCount; i++) {
109            mDefaults.put(prototype.mDefaults.keyAt(i), prototype.mDefaults.valueAt(i));
110        }
111        mDefaultMargins = prototype.mDefaultMargins;
112    }
113
114    /**
115     * Get the globally unique printer id.
116     *
117     * @return The printer id.
118     */
119    public PrinterId getId() {
120        return mId;
121    }
122
123    /**
124     * Gets the human readable printer label.
125     *
126     * @return The human readable label.
127     */
128    public CharSequence getLabel() {
129        return mLabel;
130    }
131
132    /**
133     * Gets the status of the printer.
134     *
135     * @return The status.
136     */
137    public int getStatus() {
138        return mStatus;
139    }
140
141    /**
142     * Gets the supported media sizes.
143     *
144     * @return The supported media sizes.
145     */
146    public List<MediaSize> getMediaSizes() {
147        return mMediaSizes;
148    }
149
150    /**
151     * Gets the supported resolutions.
152     *
153     * @return The supported resolutions.
154     */
155    public List<Resolution> getResolutions() {
156        return mResolutions;
157    }
158
159    /**
160     * Gets the minimal supported margins.
161     *
162     * @return The minimal margins.
163     */
164    public Margins getMinMargins() {
165        return mMinMargins;
166    }
167
168    /**
169     * Gets the available input trays.
170     *
171     * @return The input trays.
172     */
173    public List<Tray> getInputTrays() {
174        return mInputTrays;
175    }
176
177    /**
178     * Gets the available output trays.
179     *
180     * @return The output trays.
181     */
182    public List<Tray> getOutputTrays() {
183        return mOutputTrays;
184    }
185
186    /**
187     * Gets the supported duplex modes.
188     *
189     * @return The duplex modes.
190     *
191     * @see PrintAttributes#DUPLEX_MODE_NONE
192     * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
193     * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
194     */
195    public int getDuplexModes() {
196        return mDuplexModes;
197    }
198
199    /**
200     * Gets the supported color modes.
201     *
202     * @return The color modes.
203     *
204     * @see PrintAttributes#COLOR_MODE_COLOR
205     * @see PrintAttributes#COLOR_MODE_MONOCHROME
206     */
207    public int getColorModes() {
208        return mColorModes;
209    }
210
211    /**
212     * Gets the supported fitting modes.
213     *
214     * @return The fitting modes.
215     *
216     * @see PrintAttributes#FITTING_MODE_NONE
217     * @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
218     */
219    public int getFittingModes() {
220        return mFittingModes;
221    }
222
223    /**
224     * Gets the supported orientations.
225     *
226     * @return The orientations.
227     *
228     * @see PrintAttributes#ORIENTATION_PORTRAIT
229     * @see PrintAttributes#ORIENTATION_LANDSCAPE
230     */
231    public int getOrientations() {
232        return mOrientations;
233    }
234
235    /**
236     * Gets the default print attributes.
237     *
238     * @param outAttributes The attributes to populated.
239     */
240    public void getDefaults(PrintAttributes outAttributes) {
241        outAttributes.clear();
242
243        // TODO: Do we want a printer to specify default copies?
244        outAttributes.setCopies(MIN_COPIES);
245
246        outAttributes.setMargins(mDefaultMargins);
247
248        final int mediaSizeIndex = mDefaults.get(PROPERTY_MEDIA_SIZE);
249        if (mediaSizeIndex >= 0) {
250            outAttributes.setMediaSize(mMediaSizes.get(mediaSizeIndex));
251        }
252
253        final int resolutionIndex = mDefaults.get(PROPERTY_RESOLUTION);
254        if (resolutionIndex >= 0) {
255            outAttributes.setResolution(mResolutions.get(resolutionIndex));
256        }
257
258        final int inputTrayIndex = mDefaults.get(PROPERTY_INPUT_TRAY);
259        if (inputTrayIndex >= 0) {
260            outAttributes.setInputTray(mInputTrays.get(inputTrayIndex));
261        }
262
263        final int outputTrayIndex = mDefaults.get(PROPERTY_OUTPUT_TRAY);
264        if (outputTrayIndex >= 0) {
265            outAttributes.setOutputTray(mOutputTrays.get(outputTrayIndex));
266        }
267
268        final int duplexMode = mDefaults.get(PROPERTY_DUPLEX_MODE);
269        if (duplexMode > 0) {
270            outAttributes.setDuplexMode(duplexMode);
271        }
272
273        final int colorMode = mDefaults.get(PROPERTY_COLOR_MODE);
274        if (colorMode > 0) {
275            outAttributes.setColorMode(mColorModes & colorMode);
276        }
277
278        final int fittingMode = mDefaults.get(PROPERTY_FITTING_MODE);
279        if (fittingMode > 0) {
280            outAttributes.setFittingMode(fittingMode);
281        }
282
283        final int orientation = mDefaults.get(PROPERTY_ORIENTATION);
284        if (orientation > 0) {
285            outAttributes.setOrientation(orientation);
286        }
287    }
288
289    private PrinterInfo(Parcel parcel) {
290        mId = parcel.readParcelable(null);
291        mLabel = parcel.readCharSequence();
292        mStatus = parcel.readInt();
293
294        mMinMargins = readMargins(parcel);
295        readMediaSizes(parcel);
296        readResolutions(parcel);
297        mInputTrays = readInputTrays(parcel);
298        mOutputTrays = readOutputTrays(parcel);
299
300        mColorModes = parcel.readInt();
301        mDuplexModes = parcel.readInt();
302        mFittingModes = parcel.readInt();
303        mOrientations = parcel.readInt();
304
305        readDefaults(parcel);
306        mDefaultMargins = readMargins(parcel);
307    }
308
309    @Override
310    public int describeContents() {
311        return 0;
312    }
313
314    @Override
315    public void writeToParcel(Parcel parcel, int flags) {
316        parcel.writeParcelable(mId, flags);
317        parcel.writeCharSequence(mLabel);
318        parcel.writeInt(mStatus);
319
320        writeMargins(mMinMargins, parcel);
321        writeMediaSizes(parcel);
322        writeResolutions(parcel);
323        writeInputTrays(parcel);
324        writeOutputTrays(parcel);
325
326        parcel.writeInt(mColorModes);
327        parcel.writeInt(mDuplexModes);
328        parcel.writeInt(mFittingModes);
329        parcel.writeInt(mOrientations);
330
331        writeDefaults(parcel);
332        writeMargins(mDefaultMargins, parcel);
333    }
334
335    @Override
336    public String toString() {
337        StringBuilder builder = new StringBuilder();
338        builder.append("PrinterInfo{");
339        builder.append(mId).append(", \"");
340        builder.append(mLabel);
341        builder.append("\"}");
342        return builder.toString();
343    }
344
345    private void writeMediaSizes(Parcel parcel) {
346        if (mMediaSizes == null) {
347            parcel.writeInt(0);
348            return;
349        }
350        final int mediaSizeCount = mMediaSizes.size();
351        parcel.writeInt(mediaSizeCount);
352        for (int i = 0; i < mediaSizeCount; i++) {
353            mMediaSizes.get(i).writeToParcel(parcel);
354        }
355    }
356
357    private void readMediaSizes(Parcel parcel) {
358        final int mediaSizeCount = parcel.readInt();
359        for (int i = 0; i < mediaSizeCount; i++) {
360            mMediaSizes.add(MediaSize.createFromParcel(parcel));
361        }
362    }
363
364    private void writeResolutions(Parcel parcel) {
365        final int resolutionCount = mResolutions.size();
366        parcel.writeInt(resolutionCount);
367        for (int i = 0; i < resolutionCount; i++) {
368            mResolutions.get(i).writeToParcel(parcel);
369        }
370    }
371
372    private void readResolutions(Parcel parcel) {
373        final int resolutionCount = parcel.readInt();
374        for (int i = 0; i < resolutionCount; i++) {
375            mResolutions.add(Resolution.createFromParcel(parcel));
376        }
377    }
378
379    private void writeMargins(Margins margins, Parcel parcel) {
380        if (margins == null) {
381            parcel.writeInt(0);
382        } else {
383            parcel.writeInt(1);
384            margins.writeToParcel(parcel);
385        }
386    }
387
388    private Margins readMargins(Parcel parcel) {
389        return (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
390    }
391
392    private void writeInputTrays(Parcel parcel) {
393        if (mInputTrays == null) {
394            parcel.writeInt(0);
395            return;
396        }
397        final int inputTrayCount = mInputTrays.size();
398        parcel.writeInt(inputTrayCount);
399        for (int i = 0; i < inputTrayCount; i++) {
400            mInputTrays.get(i).writeToParcel(parcel);
401        }
402    }
403
404    private List<Tray> readInputTrays(Parcel parcel) {
405        final int inputTrayCount = parcel.readInt();
406        if (inputTrayCount <= 0) {
407            return null;
408        }
409        List<Tray> inputTrays = new ArrayList<Tray>(inputTrayCount);
410        for (int i = 0; i < inputTrayCount; i++) {
411            inputTrays.add(Tray.createFromParcel(parcel));
412        }
413        return inputTrays;
414    }
415
416    private void writeOutputTrays(Parcel parcel) {
417        if (mOutputTrays == null) {
418            parcel.writeInt(0);
419            return;
420        }
421        final int outputTrayCount = mOutputTrays.size();
422        parcel.writeInt(outputTrayCount);
423        for (int i = 0; i < outputTrayCount; i++) {
424            mOutputTrays.get(i).writeToParcel(parcel);
425        }
426    }
427
428    private List<Tray> readOutputTrays(Parcel parcel) {
429        final int outputTrayCount = parcel.readInt();
430        if (outputTrayCount <= 0) {
431            return null;
432        }
433        List<Tray> outputTrays = new ArrayList<Tray>(outputTrayCount);
434        for (int i = 0; i < outputTrayCount; i++) {
435            outputTrays.add(Tray.createFromParcel(parcel));
436        }
437        return outputTrays;
438    }
439
440    private void readDefaults(Parcel parcel) {
441        final int defaultCount = parcel.readInt();
442        for (int i = 0; i < defaultCount; i++) {
443            mDefaults.append(mDefaults.size(), parcel.readInt());
444        }
445    }
446
447    private void writeDefaults(Parcel parcel) {
448        final int defaultCount = mDefaults.size();
449        parcel.writeInt(defaultCount);
450        for (int i = 0; i < defaultCount; i++) {
451            parcel.writeInt(mDefaults.valueAt(i));
452        }
453    }
454
455    /**
456     * Builder for creating of a {@link PrinterInfo}. This class is responsible
457     * to enforce that all required attributes have at least one default value.
458     * In other words, this class creates only well-formed {@link PrinterInfo}s.
459     * <p>
460     * Look at the individual methods for a reference whether a property is
461     * required or if it is optional.
462     * </p>
463     */
464    public static final class Builder {
465        private final PrinterInfo mPrototype;
466
467        /**
468         * Creates a new instance.
469         *
470         * @param printerId The printer id.
471         * @param label The human readable printer label.
472         *
473         * @throws IllegalArgumentException IF the printer id is null.
474         * @throws IllegalArgumentException IF the label is empty.
475         */
476        public Builder(PrinterId printerId, CharSequence label) {
477            if (printerId == null) {
478                throw new IllegalArgumentException("printerId cannot be null.");
479            }
480            if (TextUtils.isEmpty(label)) {
481                throw new IllegalArgumentException("label cannot be empty.");
482            }
483            mPrototype = new PrinterInfo();
484            mPrototype.mLabel = label;
485            mPrototype.mId = printerId;
486        }
487
488        /**
489         * Sets the printer status.
490         * <p>
491         * <strong>Required:</strong> Yes
492         * </p>
493         *
494         * @param status The status.
495         * @return This builder.
496         */
497        public Builder setStatus(int status) {
498            mPrototype.mStatus = status;
499            return this;
500        }
501
502        /**
503         * Adds a supported media size.
504         * <p>
505         * <strong>Required:</strong> Yes
506         * </p>
507         *
508         * @param mediaSize A media size.
509         * @param isDefault Whether this is the default.
510         * @return This builder.
511         * @throws IllegalArgumentException If set as default and there
512         *     is already a default.
513         *
514         * @see PrintAttributes.MediaSize
515         */
516        public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
517            final int insertionIndex = mPrototype.mMediaSizes.size();
518            mPrototype.mMediaSizes.add(mediaSize);
519            if (isDefault) {
520                throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
521                mPrototype.mDefaults.put(PROPERTY_MEDIA_SIZE, insertionIndex);
522            }
523            return this;
524        }
525
526        /**
527         * Adds a supported resolution.
528         * <p>
529         * <strong>Required:</strong> Yes
530         * </p>
531         *
532         * @param resolution A resolution.
533         * @param isDefault Whether this is the default.
534         * @return This builder.
535         *
536         * @throws IllegalArgumentException If set as default and there
537         *     is already a default.
538         *
539         * @see PrintAttributes.Resolution
540         */
541        public Builder addResolution(Resolution resolution, boolean isDefault) {
542            final int insertionIndex = mPrototype.mResolutions.size();
543            mPrototype.mResolutions.add(resolution);
544            if (isDefault) {
545                throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
546                mPrototype.mDefaults.put(PROPERTY_RESOLUTION, insertionIndex);
547            }
548            return this;
549        }
550
551        /**
552         * Sets the minimal margins.
553         * <p>
554         * <strong>Required:</strong> No
555         * </p>
556         *
557         * @param margins The margins.
558         * @param defaultMargins The default margins.
559         * @return This builder.
560         *
561         * @see PrintAttributes.Margins
562         */
563        public Builder setMinMargins(Margins margins, Margins defaultMargins) {
564            if (margins.getLeftMils() > defaultMargins.getLeftMils()
565                    || margins.getTopMils() > defaultMargins.getTopMils()
566                    || margins.getRightMils() < defaultMargins.getRightMils()
567                    || margins.getBottomMils() < defaultMargins.getBottomMils()) {
568                throw new IllegalArgumentException("Default margins"
569                    + " cannot be outside of the min margins.");
570            }
571            mPrototype.mMinMargins = margins;
572            mPrototype.mDefaultMargins = defaultMargins;
573            return this;
574        }
575
576        /**
577         * Adds an input tray.
578         * <p>
579         * <strong>Required:</strong> No
580         * </p>
581         *
582         * @param inputTray A tray.
583         * @param isDefault Whether this is the default.
584         * @return This builder.
585         *
586         * @throws IllegalArgumentException If set as default and there
587         *     is already a default.
588         *
589         * @see PrintAttributes.Tray
590         */
591        public Builder addInputTray(Tray inputTray, boolean isDefault) {
592            if (mPrototype.mInputTrays == null) {
593                mPrototype.mInputTrays = new ArrayList<Tray>();
594            }
595            final int insertionIndex = mPrototype.mInputTrays.size();
596            mPrototype.mInputTrays.add(inputTray);
597            if (isDefault) {
598                throwIfDefaultAlreadySpecified(PROPERTY_INPUT_TRAY);
599                mPrototype.mDefaults.put(PROPERTY_INPUT_TRAY, insertionIndex);
600            }
601            return this;
602        }
603
604        /**
605         * Adds an output tray.
606         * <p>
607         * <strong>Required:</strong> No
608         * </p>
609         *
610         * @param outputTray A tray.
611         * @param isDefault Whether this is the default.
612         * @return This builder.
613         *
614         * @throws IllegalArgumentException If set as default and there
615         *     is already a default.
616         *
617         * @see PrintAttributes.Tray
618         */
619        public Builder addOutputTray(Tray outputTray, boolean isDefault) {
620            if (mPrototype.mOutputTrays == null) {
621                mPrototype.mOutputTrays = new ArrayList<Tray>();
622            }
623            final int insertionIndex = mPrototype.mOutputTrays.size();
624            mPrototype.mOutputTrays.add(outputTray);
625            if (isDefault) {
626                throwIfDefaultAlreadySpecified(PROPERTY_OUTPUT_TRAY);
627                mPrototype.mDefaults.put(PROPERTY_OUTPUT_TRAY, insertionIndex);
628            }
629            return this;
630        }
631
632        /**
633         * Sets the color modes.
634         * <p>
635         * <strong>Required:</strong> Yes
636         * </p>
637         *
638         * @param colorModes The color mode bit mask.
639         * @param defaultColorMode The default color mode.
640         * @return This builder.
641         *
642         * @throws IllegalArgumentException If color modes contains an invalid
643         *         mode bit or if the default color mode is invalid.
644         *
645         * @see PrintAttributes#COLOR_MODE_COLOR
646         * @see PrintAttributes#COLOR_MODE_MONOCHROME
647         */
648        public Builder setColorModes(int colorModes, int defaultColorMode) {
649            int currentModes = colorModes;
650            while (currentModes > 0) {
651                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
652                currentModes &= ~currentMode;
653                PrintAttributes.enforceValidColorMode(currentMode);
654            }
655            if ((colorModes & defaultColorMode) == 0) {
656                throw new IllegalArgumentException("Default color mode not in color modes.");
657            }
658            PrintAttributes.enforceValidColorMode(colorModes);
659            mPrototype.mColorModes = colorModes;
660            mPrototype.mDefaults.put(PROPERTY_COLOR_MODE, defaultColorMode);
661            return this;
662        }
663
664        /**
665         * Set the duplex modes.
666         * <p>
667         * <strong>Required:</strong> No
668         * </p>
669         *
670         * @param duplexModes The duplex mode bit mask.
671         * @param defaultDuplexMode The default duplex mode.
672         * @return This builder.
673         *
674         * @throws IllegalArgumentException If duplex modes contains an invalid
675         *         mode bit or if the default duplex mode is invalid.
676         *
677         * @see PrintAttributes#DUPLEX_MODE_NONE
678         * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
679         * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
680         */
681        public Builder setDuplexModes(int duplexModes, int defaultDuplexMode) {
682            int currentModes = duplexModes;
683            while (currentModes > 0) {
684                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
685                currentModes &= ~currentMode;
686                PrintAttributes.enforceValidDuplexMode(currentMode);
687            }
688            if ((duplexModes & defaultDuplexMode) == 0) {
689                throw new IllegalArgumentException("Default duplex mode not in duplex modes.");
690            }
691            PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
692            mPrototype.mDuplexModes = duplexModes;
693            mPrototype.mDefaults.put(PROPERTY_DUPLEX_MODE, defaultDuplexMode);
694            return this;
695        }
696
697        /**
698         * Sets the fitting modes.
699         * <p>
700         * <strong>Required:</strong> No
701         * </p>
702         *
703         * @param fittingModes The fitting mode bit mask.
704         * @param defaultFittingMode The default fitting mode.
705         * @return This builder.
706         *
707         * @throws IllegalArgumentException If fitting modes contains an invalid
708         *         mode bit or if the default fitting mode is invalid.
709         *
710         * @see PrintAttributes#FITTING_MODE_NONE
711         * @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
712         */
713        public Builder setFittingModes(int fittingModes, int defaultFittingMode) {
714            int currentModes = fittingModes;
715            while (currentModes > 0) {
716                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
717                currentModes &= ~currentMode;
718                PrintAttributes.enfoceValidFittingMode(currentMode);
719            }
720            if ((fittingModes & defaultFittingMode) == 0) {
721                throw new IllegalArgumentException("Default fitting mode not in fiting modes.");
722            }
723            PrintAttributes.enfoceValidFittingMode(defaultFittingMode);
724            mPrototype.mFittingModes = fittingModes;
725            mPrototype.mDefaults.put(PROPERTY_FITTING_MODE, defaultFittingMode);
726            return this;
727        }
728
729        /**
730         * Sets the orientations.
731         * <p>
732         * <strong>Required:</strong> Yes
733         * </p>
734         *
735         * @param orientations The orientation bit mask.
736         * @param defaultOrientation The default orientation.
737         * @return This builder.
738         *
739         * @throws IllegalArgumentException If orientations contains an invalid
740         *         mode bit or if the default orientation is invalid.
741         *
742         * @see PrintAttributes#ORIENTATION_PORTRAIT
743         * @see PrintAttributes#ORIENTATION_LANDSCAPE
744         */
745        public Builder setOrientations(int orientations, int defaultOrientation) {
746            int currentOrientaions = orientations;
747            while (currentOrientaions > 0) {
748                final int currentOrnt = (1 << Integer.numberOfTrailingZeros(currentOrientaions));
749                currentOrientaions &= ~currentOrnt;
750                PrintAttributes.enforceValidOrientation(currentOrnt);
751            }
752            if ((orientations & defaultOrientation) == 0) {
753                throw new IllegalArgumentException("Default orientation not in orientations.");
754            }
755            PrintAttributes.enforceValidOrientation(defaultOrientation);
756            mPrototype.mOrientations = orientations;
757            mPrototype.mDefaults.put(PROPERTY_ORIENTATION, defaultOrientation);
758            return this;
759        }
760
761        /**
762         * Crates a new {@link PrinterInfo} enforcing that all required properties
763         * have need specified. See individual methods in this class for reference
764         * about required attributes.
765         *
766         * @return A new {@link PrinterInfo}.
767         *
768         * @throws IllegalStateException If a required attribute was not specified.
769         */
770        public PrinterInfo create() {
771            if (mPrototype.mMediaSizes == null || mPrototype.mMediaSizes.isEmpty()) {
772                throw new IllegalStateException("No media size specified.");
773            }
774            if (mPrototype.mDefaults.valueAt(PROPERTY_MEDIA_SIZE) == DEFAULT_UNDEFINED) {
775                throw new IllegalStateException("No default media size specified.");
776            }
777            if (mPrototype.mResolutions == null || mPrototype.mResolutions.isEmpty()) {
778                throw new IllegalStateException("No resolution specified.");
779            }
780            if (mPrototype.mDefaults.valueAt(PROPERTY_RESOLUTION) == DEFAULT_UNDEFINED) {
781                throw new IllegalStateException("No default resolution specified.");
782            }
783            if (mPrototype.mColorModes == 0) {
784                throw new IllegalStateException("No color mode specified.");
785            }
786            if (mPrototype.mDefaults.valueAt(PROPERTY_COLOR_MODE) == DEFAULT_UNDEFINED) {
787                throw new IllegalStateException("No default color mode specified.");
788            }
789            if (mPrototype.mOrientations == 0) {
790                throw new IllegalStateException("No oprientation specified.");
791            }
792            if (mPrototype.mDefaults.valueAt(PROPERTY_ORIENTATION) == DEFAULT_UNDEFINED) {
793                throw new IllegalStateException("No default orientation specified.");
794            }
795            if (mPrototype.mMinMargins == null) {
796                mPrototype.mMinMargins  = new Margins(0, 0, 0, 0);
797            }
798            if (mPrototype.mDefaultMargins == null) {
799                mPrototype.mDefaultMargins = mPrototype.mMinMargins;
800            }
801            return new PrinterInfo(mPrototype);
802        }
803
804        private void throwIfDefaultAlreadySpecified(int propertyIndex) {
805            if (mPrototype.mDefaults.get(propertyIndex) != DEFAULT_UNDEFINED) {
806                throw new IllegalArgumentException("Default already specified.");
807            }
808        }
809    }
810
811    public static final Parcelable.Creator<PrinterInfo> CREATOR =
812            new Parcelable.Creator<PrinterInfo>() {
813        @Override
814        public PrinterInfo createFromParcel(Parcel parcel) {
815            return new PrinterInfo(parcel);
816        }
817
818        @Override
819        public PrinterInfo[] newArray(int size) {
820            return new PrinterInfo[size];
821        }
822    };
823}
824