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 */ PrinterCapabilitiesInfo()65 public PrinterCapabilitiesInfo() { 66 Arrays.fill(mDefaults, DEFAULT_UNDEFINED); 67 } 68 69 /** 70 * @hide 71 */ PrinterCapabilitiesInfo(PrinterCapabilitiesInfo prototype)72 public PrinterCapabilitiesInfo(PrinterCapabilitiesInfo prototype) { 73 copyFrom(prototype); 74 } 75 76 /** 77 * @hide 78 */ copyFrom(PrinterCapabilitiesInfo other)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 */ getMediaSizes()121 public List<MediaSize> getMediaSizes() { 122 return Collections.unmodifiableList(mMediaSizes); 123 } 124 125 /** 126 * Gets the supported resolutions. 127 * 128 * @return The resolutions. 129 */ getResolutions()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 */ getMinMargins()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 */ getColorModes()152 public int getColorModes() { 153 return mColorModes; 154 } 155 156 /** 157 * Gets the default print attributes. 158 * 159 * @return The default attributes. 160 */ getDefaults()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 PrinterCapabilitiesInfo(Parcel parcel)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 describeContents()195 public int describeContents() { 196 return 0; 197 } 198 199 @Override writeToParcel(Parcel parcel, int flags)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 hashCode()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 equals(Object obj)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 toString()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 colorModesToString()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 writeMediaSizes(Parcel parcel)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 readMediaSizes(Parcel parcel)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 writeResolutions(Parcel parcel)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 readResolutions(Parcel parcel)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 writeMargins(Margins margins, Parcel parcel)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 readMargins(Parcel parcel)345 private Margins readMargins(Parcel parcel) { 346 return (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null; 347 } 348 readDefaults(Parcel parcel)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 writeDefaults(Parcel parcel)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 */ Builder(PrinterId printerId)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 */ addMediaSize(MediaSize mediaSize, boolean isDefault)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 */ addResolution(Resolution resolution, boolean isDefault)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 */ setMinMargins(Margins margins)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 */ setColorModes(int colorModes, int defaultColorMode)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 */ build()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 throwIfDefaultAlreadySpecified(int propertyIndex)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