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