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