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