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