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