PrinterCapabilitiesInfo.java revision 773f54de3de9bce7b6f915aa47ed686b161d77aa
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;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
28
29/**
30 * This class represents the capabilities of a printer.
31 */
32public final class PrinterCapabilitiesInfo implements Parcelable {
33    /**
34     * Undefined default value.
35     *
36     * @hide
37     */
38    public static final int DEFAULT_UNDEFINED = -1;
39
40    private static final int PROPERTY_MEDIA_SIZE = 0;
41    private static final int PROPERTY_RESOLUTION = 1;
42    private static final int PROPERTY_COLOR_MODE = 2;
43    private static final int PROPERTY_COUNT = 3;
44
45    private static final Margins DEFAULT_MARGINS = new Margins(0,  0,  0,  0);
46
47    private Margins mMinMargins = DEFAULT_MARGINS;
48    private List<MediaSize> mMediaSizes;
49    private List<Resolution> mResolutions;
50
51    private int mColorModes;
52
53    private final int[] mDefaults = new int[PROPERTY_COUNT];
54    private Margins mDefaultMargins = DEFAULT_MARGINS;
55
56    /**
57     * @hide
58     */
59    public PrinterCapabilitiesInfo() {
60        Arrays.fill(mDefaults, DEFAULT_UNDEFINED);
61    }
62
63    /**
64     * @hide
65     */
66    public PrinterCapabilitiesInfo(PrinterCapabilitiesInfo prototype) {
67        copyFrom(prototype);
68    }
69
70    /**
71     * @hide
72     */
73    public void copyFrom(PrinterCapabilitiesInfo other) {
74        mMinMargins = other.mMinMargins;
75
76        if (other.mMediaSizes != null) {
77            if (mMediaSizes != null) {
78                mMediaSizes.clear();
79                mMediaSizes.addAll(other.mMediaSizes);
80            } else {
81                mMediaSizes = new ArrayList<MediaSize>(other.mMediaSizes);
82            }
83        } else {
84            mMediaSizes = null;
85        }
86
87        if (other.mResolutions != null) {
88            if (mResolutions != null) {
89                mResolutions.clear();
90                mResolutions.addAll(other.mResolutions);
91            } else {
92                mResolutions = new ArrayList<Resolution>(other.mResolutions);
93            }
94        } else {
95            mResolutions = null;
96        }
97
98        mColorModes = other.mColorModes;
99
100        final int defaultCount = other.mDefaults.length;
101        for (int i = 0; i < defaultCount; i++) {
102            mDefaults[i] = other.mDefaults[i];
103        }
104
105        mDefaultMargins = other.mDefaultMargins;
106    }
107
108    /**
109     * Gets the supported media sizes.
110     *
111     * @return The media sizes.
112     */
113    public List<MediaSize> getMediaSizes() {
114        return mMediaSizes;
115    }
116
117    /**
118     * Gets the supported resolutions.
119     *
120     * @return The resolutions.
121     */
122    public List<Resolution> getResolutions() {
123        return mResolutions;
124    }
125
126    /**
127     * Gets the minimal supported margins.
128     *
129     * @return The minimal margins.
130     */
131    public Margins getMinMargins() {
132        return mMinMargins;
133    }
134
135    /**
136     * Gets the supported color modes.
137     *
138     * @return The color modes.
139     *
140     * @see PrintAttributes#COLOR_MODE_COLOR
141     * @see PrintAttributes#COLOR_MODE_MONOCHROME
142     */
143    public int getColorModes() {
144        return mColorModes;
145    }
146
147    /**
148     * Gets the default print attributes.
149     *
150     * @param outAttributes The attributes to populated.
151     */
152    public void getDefaults(PrintAttributes outAttributes) {
153        outAttributes.clear();
154
155        outAttributes.setMargins(mDefaultMargins);
156
157        final int mediaSizeIndex = mDefaults[PROPERTY_MEDIA_SIZE];
158        if (mediaSizeIndex >= 0) {
159            outAttributes.setMediaSize(mMediaSizes.get(mediaSizeIndex));
160        }
161
162        final int resolutionIndex = mDefaults[PROPERTY_RESOLUTION];
163        if (resolutionIndex >= 0) {
164            outAttributes.setResolution(mResolutions.get(resolutionIndex));
165        }
166
167        final int colorMode = mDefaults[PROPERTY_COLOR_MODE];
168        if (colorMode > 0) {
169            outAttributes.setColorMode(colorMode);
170        }
171    }
172
173    private PrinterCapabilitiesInfo(Parcel parcel) {
174        mMinMargins = readMargins(parcel);
175        readMediaSizes(parcel);
176        readResolutions(parcel);
177
178        mColorModes = parcel.readInt();
179
180        readDefaults(parcel);
181        mDefaultMargins = readMargins(parcel);
182    }
183
184    @Override
185    public int describeContents() {
186        return 0;
187    }
188
189    @Override
190    public void writeToParcel(Parcel parcel, int flags) {
191        writeMargins(mMinMargins, parcel);
192        writeMediaSizes(parcel);
193        writeResolutions(parcel);
194
195        parcel.writeInt(mColorModes);
196
197        writeDefaults(parcel);
198        writeMargins(mDefaultMargins, parcel);
199    }
200
201    @Override
202    public int hashCode() {
203        final int prime = 31;
204        int result = 1;
205        result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
206        result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode());
207        result = prime * result + ((mResolutions == null) ? 0 : mResolutions.hashCode());
208        result = prime * result + mColorModes;
209        result = prime * result + Arrays.hashCode(mDefaults);
210        result = prime * result + ((mDefaultMargins == null) ? 0 : mDefaultMargins.hashCode());
211        return result;
212    }
213
214    @Override
215    public boolean equals(Object obj) {
216        if (this == obj) {
217            return true;
218        }
219        if (obj == null) {
220            return false;
221        }
222        if (getClass() != obj.getClass()) {
223            return false;
224        }
225        PrinterCapabilitiesInfo other = (PrinterCapabilitiesInfo) obj;
226        if (mMinMargins == null) {
227            if (other.mMinMargins != null) {
228                return false;
229            }
230        } else if (!mMinMargins.equals(other.mMinMargins)) {
231            return false;
232        }
233        if (mMediaSizes == null) {
234            if (other.mMediaSizes != null) {
235                return false;
236            }
237        } else if (!mMediaSizes.equals(other.mMediaSizes)) {
238            return false;
239        }
240        if (mResolutions == null) {
241            if (other.mResolutions != null) {
242                return false;
243            }
244        } else if (!mResolutions.equals(other.mResolutions)) {
245            return false;
246        }
247        if (mColorModes != other.mColorModes) {
248            return false;
249        }
250        if (!Arrays.equals(mDefaults, other.mDefaults)) {
251            return false;
252        }
253        if (mDefaultMargins == null) {
254            if (other.mDefaultMargins != null) {
255                return false;
256            }
257        } else if (!mDefaultMargins.equals(other.mDefaultMargins)) {
258            return false;
259        }
260        return true;
261    }
262
263    @Override
264    public String toString() {
265        StringBuilder builder = new StringBuilder();
266        builder.append("PrinterInfo{");
267        builder.append("minMargins=").append(mMinMargins);
268        builder.append(", mediaSizes=").append(mMediaSizes);
269        builder.append(", resolutions=").append(mResolutions);
270        builder.append(", colorModes=").append(colorModesToString());
271        builder.append("\"}");
272        return builder.toString();
273    }
274
275    private String colorModesToString() {
276        StringBuilder builder = new StringBuilder();
277        builder.append('[');
278        int colorModes = mColorModes;
279        while (colorModes != 0) {
280            final int colorMode = 1 << Integer.numberOfTrailingZeros(colorModes);
281            colorModes &= ~colorMode;
282            if (builder.length() > 0) {
283                builder.append(", ");
284            }
285            builder.append(PrintAttributes.colorModeToString(colorMode));
286        }
287        builder.append(']');
288        return builder.toString();
289    }
290
291    private void writeMediaSizes(Parcel parcel) {
292        if (mMediaSizes == null) {
293            parcel.writeInt(0);
294            return;
295        }
296        final int mediaSizeCount = mMediaSizes.size();
297        parcel.writeInt(mediaSizeCount);
298        for (int i = 0; i < mediaSizeCount; i++) {
299            mMediaSizes.get(i).writeToParcel(parcel);
300        }
301    }
302
303    private void readMediaSizes(Parcel parcel) {
304        final int mediaSizeCount = parcel.readInt();
305        if (mediaSizeCount > 0 && mMediaSizes == null) {
306            mMediaSizes = new ArrayList<MediaSize>();
307        }
308        for (int i = 0; i < mediaSizeCount; i++) {
309            mMediaSizes.add(MediaSize.createFromParcel(parcel));
310        }
311    }
312
313    private void writeResolutions(Parcel parcel) {
314        if (mResolutions == null) {
315            parcel.writeInt(0);
316            return;
317        }
318        final int resolutionCount = mResolutions.size();
319        parcel.writeInt(resolutionCount);
320        for (int i = 0; i < resolutionCount; i++) {
321            mResolutions.get(i).writeToParcel(parcel);
322        }
323    }
324
325    private void readResolutions(Parcel parcel) {
326        final int resolutionCount = parcel.readInt();
327        if (resolutionCount > 0 && mResolutions == null) {
328            mResolutions = new ArrayList<Resolution>();
329        }
330        for (int i = 0; i < resolutionCount; i++) {
331            mResolutions.add(Resolution.createFromParcel(parcel));
332        }
333    }
334
335    private void writeMargins(Margins margins, Parcel parcel) {
336        if (margins == null) {
337            parcel.writeInt(0);
338        } else {
339            parcel.writeInt(1);
340            margins.writeToParcel(parcel);
341        }
342    }
343
344    private Margins readMargins(Parcel parcel) {
345        return (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
346    }
347
348    private void readDefaults(Parcel parcel) {
349        final int defaultCount = parcel.readInt();
350        for (int i = 0; i < defaultCount; i++) {
351            mDefaults[i] = parcel.readInt();
352        }
353    }
354
355    private void writeDefaults(Parcel parcel) {
356        final int defaultCount = mDefaults.length;
357        parcel.writeInt(defaultCount);
358        for (int i = 0; i < defaultCount; i++) {
359            parcel.writeInt(mDefaults[i]);
360        }
361    }
362
363    /**
364     * Builder for creating of a {@link PrinterInfo}. This class is responsible
365     * to enforce that all required attributes have at least one default value.
366     * In other words, this class creates only well-formed {@link PrinterInfo}s.
367     * <p>
368     * Look at the individual methods for a reference whether a property is
369     * required or if it is optional.
370     * </p>
371     */
372    public static final class Builder {
373        private final PrinterCapabilitiesInfo mPrototype;
374
375        /**
376         * Creates a new instance.
377         *
378         * @param printerId The printer id. Cannot be null.
379         *
380         * @throws IllegalArgumentException If the printer id is null.
381         */
382        public Builder(PrinterId printerId) {
383            if (printerId == null) {
384                throw new IllegalArgumentException("printerId cannot be null.");
385            }
386            mPrototype = new PrinterCapabilitiesInfo();
387        }
388
389        /**
390         * Adds a supported media size.
391         * <p>
392         * <strong>Required:</strong> Yes
393         * </p>
394         *
395         * @param mediaSize A media size.
396         * @param isDefault Whether this is the default.
397         * @return This builder.
398         * @throws IllegalArgumentException If set as default and there
399         *     is already a default.
400         *
401         * @see PrintAttributes.MediaSize
402         */
403        public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
404            if (mPrototype.mMediaSizes == null) {
405                mPrototype.mMediaSizes = new ArrayList<MediaSize>();
406            }
407            final int insertionIndex = mPrototype.mMediaSizes.size();
408            mPrototype.mMediaSizes.add(mediaSize);
409            if (isDefault) {
410                throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
411                mPrototype.mDefaults[PROPERTY_MEDIA_SIZE] = insertionIndex;
412            }
413            return this;
414        }
415
416        /**
417         * Adds a supported resolution.
418         * <p>
419         * <strong>Required:</strong> Yes
420         * </p>
421         *
422         * @param resolution A resolution.
423         * @param isDefault Whether this is the default.
424         * @return This builder.
425         *
426         * @throws IllegalArgumentException If set as default and there
427         *     is already a default.
428         *
429         * @see PrintAttributes.Resolution
430         */
431        public Builder addResolution(Resolution resolution, boolean isDefault) {
432            if (mPrototype.mResolutions == null) {
433                mPrototype.mResolutions = new ArrayList<Resolution>();
434            }
435            final int insertionIndex = mPrototype.mResolutions.size();
436            mPrototype.mResolutions.add(resolution);
437            if (isDefault) {
438                throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
439                mPrototype.mDefaults[PROPERTY_RESOLUTION] = insertionIndex;
440            }
441            return this;
442        }
443
444        /**
445         * Sets the minimal margins.
446         * <p>
447         * <strong>Required:</strong> No
448         * </p>
449         *
450         * @param margins The margins.
451         * @param defaultMargins The default margins.
452         * @return This builder.
453         *
454         * @see PrintAttributes.Margins
455         */
456        public Builder setMinMargins(Margins margins, Margins defaultMargins) {
457            if (margins.getLeftMils() > defaultMargins.getLeftMils()
458                    || margins.getTopMils() > defaultMargins.getTopMils()
459                    || margins.getRightMils() < defaultMargins.getRightMils()
460                    || margins.getBottomMils() < defaultMargins.getBottomMils()) {
461                throw new IllegalArgumentException("Default margins"
462                    + " cannot be outside of the min margins.");
463            }
464            mPrototype.mMinMargins = margins;
465            mPrototype.mDefaultMargins = defaultMargins;
466            return this;
467        }
468
469        /**
470         * Sets the color modes.
471         * <p>
472         * <strong>Required:</strong> Yes
473         * </p>
474         *
475         * @param colorModes The color mode bit mask.
476         * @param defaultColorMode The default color mode.
477         * @return This builder.
478         *
479         * @throws IllegalArgumentException If color modes contains an invalid
480         *         mode bit or if the default color mode is invalid.
481         *
482         * @see PrintAttributes#COLOR_MODE_COLOR
483         * @see PrintAttributes#COLOR_MODE_MONOCHROME
484         */
485        public Builder setColorModes(int colorModes, int defaultColorMode) {
486            int currentModes = colorModes;
487            while (currentModes > 0) {
488                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
489                currentModes &= ~currentMode;
490                PrintAttributes.enforceValidColorMode(currentMode);
491            }
492            if ((colorModes & defaultColorMode) == 0) {
493                throw new IllegalArgumentException("Default color mode not in color modes.");
494            }
495            PrintAttributes.enforceValidColorMode(colorModes);
496            mPrototype.mColorModes = colorModes;
497            mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
498            return this;
499        }
500
501        /**
502         * Crates a new {@link PrinterCapabilitiesInfo} enforcing that all
503         * required properties have need specified. See individual methods
504         * in this class for reference about required attributes.
505         *
506         * @return A new {@link PrinterCapabilitiesInfo}.
507         *
508         * @throws IllegalStateException If a required attribute was not specified.
509         */
510        public PrinterCapabilitiesInfo create() {
511            if (mPrototype.mMediaSizes == null || mPrototype.mMediaSizes.isEmpty()) {
512                throw new IllegalStateException("No media size specified.");
513            }
514            if (mPrototype.mDefaults[PROPERTY_MEDIA_SIZE] == DEFAULT_UNDEFINED) {
515                throw new IllegalStateException("No default media size specified.");
516            }
517            if (mPrototype.mResolutions == null || mPrototype.mResolutions.isEmpty()) {
518                throw new IllegalStateException("No resolution specified.");
519            }
520            if (mPrototype.mDefaults[PROPERTY_RESOLUTION] == DEFAULT_UNDEFINED) {
521                throw new IllegalStateException("No default resolution specified.");
522            }
523            if (mPrototype.mColorModes == 0) {
524                throw new IllegalStateException("No color mode specified.");
525            }
526            if (mPrototype.mDefaults[PROPERTY_COLOR_MODE] == DEFAULT_UNDEFINED) {
527                throw new IllegalStateException("No default color mode specified.");
528            }
529            if (mPrototype.mMinMargins == null) {
530                mPrototype.mMinMargins  = new Margins(0, 0, 0, 0);
531            }
532            if (mPrototype.mDefaultMargins == null) {
533                mPrototype.mDefaultMargins = mPrototype.mMinMargins;
534            }
535            return new PrinterCapabilitiesInfo(mPrototype);
536        }
537
538        private void throwIfDefaultAlreadySpecified(int propertyIndex) {
539            if (mPrototype.mDefaults[propertyIndex] != DEFAULT_UNDEFINED) {
540                throw new IllegalArgumentException("Default already specified.");
541            }
542        }
543    }
544
545    public static final Parcelable.Creator<PrinterCapabilitiesInfo> CREATOR =
546            new Parcelable.Creator<PrinterCapabilitiesInfo>() {
547        @Override
548        public PrinterCapabilitiesInfo createFromParcel(Parcel parcel) {
549            return new PrinterCapabilitiesInfo(parcel);
550        }
551
552        @Override
553        public PrinterCapabilitiesInfo[] newArray(int size) {
554            return new PrinterCapabilitiesInfo[size];
555        }
556    };
557}
558
559