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.annotation.DrawableRes; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.TestApi; 24 import android.app.PendingIntent; 25 import android.content.Context; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.PackageManager.NameNotFoundException; 30 import android.graphics.drawable.Drawable; 31 import android.graphics.drawable.Icon; 32 import android.os.Parcel; 33 import android.os.Parcelable; 34 import android.text.TextUtils; 35 36 import com.android.internal.util.Preconditions; 37 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 41 /** 42 * This class represents the description of a printer. Instances of 43 * this class are created by print services to report to the system 44 * the printers they manage. The information of this class has two 45 * major components, printer properties such as name, id, status, 46 * description and printer capabilities which describe the various 47 * print modes a printer supports such as media sizes, margins, etc. 48 * <p> 49 * Once {@link PrinterInfo.Builder#build() built} the objects are immutable. 50 * </p> 51 */ 52 public final class PrinterInfo implements Parcelable { 53 54 /** @hide */ 55 @IntDef({ 56 STATUS_IDLE, STATUS_BUSY, STATUS_UNAVAILABLE 57 }) 58 @Retention(RetentionPolicy.SOURCE) 59 public @interface Status { 60 } 61 /** Printer status: the printer is idle and ready to print. */ 62 public static final int STATUS_IDLE = 1; 63 64 /** Printer status: the printer is busy printing. */ 65 public static final int STATUS_BUSY = 2; 66 67 /** Printer status: the printer is not available. */ 68 public static final int STATUS_UNAVAILABLE = 3; 69 70 private final @NonNull PrinterId mId; 71 72 /** Resource inside the printer's services's package to be used as an icon */ 73 private final int mIconResourceId; 74 75 /** If a custom icon can be loaded for the printer */ 76 private final boolean mHasCustomPrinterIcon; 77 78 /** The generation of the icon in the cache. */ 79 private final int mCustomPrinterIconGen; 80 81 /** Intent that launches the activity showing more information about the printer. */ 82 private final @Nullable PendingIntent mInfoIntent; 83 84 private final @NonNull String mName; 85 86 private final @Status int mStatus; 87 88 private final @Nullable String mDescription; 89 90 private final @Nullable PrinterCapabilitiesInfo mCapabilities; 91 PrinterInfo(@onNull PrinterId printerId, @NonNull String name, @Status int status, int iconResourceId, boolean hasCustomPrinterIcon, String description, PendingIntent infoIntent, PrinterCapabilitiesInfo capabilities, int customPrinterIconGen)92 private PrinterInfo(@NonNull PrinterId printerId, @NonNull String name, @Status int status, 93 int iconResourceId, boolean hasCustomPrinterIcon, String description, 94 PendingIntent infoIntent, PrinterCapabilitiesInfo capabilities, 95 int customPrinterIconGen) { 96 mId = printerId; 97 mName = name; 98 mStatus = status; 99 mIconResourceId = iconResourceId; 100 mHasCustomPrinterIcon = hasCustomPrinterIcon; 101 mDescription = description; 102 mInfoIntent = infoIntent; 103 mCapabilities = capabilities; 104 mCustomPrinterIconGen = customPrinterIconGen; 105 } 106 107 /** 108 * Get the globally unique printer id. 109 * 110 * @return The printer id. 111 */ getId()112 public @NonNull PrinterId getId() { 113 return mId; 114 } 115 116 /** 117 * Get the icon to be used for this printer. If no per printer icon is available, the printer's 118 * service's icon is returned. If the printer has a custom icon this icon might get requested 119 * asynchronously. Once the icon is loaded the discovery sessions will be notified that the 120 * printer changed. 121 * 122 * @param context The context that will be using the icons 123 * @return The icon to be used for the printer or null if no icon could be found. 124 * @hide 125 */ 126 @TestApi loadIcon(@onNull Context context)127 public @Nullable Drawable loadIcon(@NonNull Context context) { 128 Drawable drawable = null; 129 PackageManager packageManager = context.getPackageManager(); 130 131 if (mHasCustomPrinterIcon) { 132 PrintManager printManager = (PrintManager) context 133 .getSystemService(Context.PRINT_SERVICE); 134 135 Icon icon = printManager.getCustomPrinterIcon(mId); 136 137 if (icon != null) { 138 drawable = icon.loadDrawable(context); 139 } 140 } 141 142 if (drawable == null) { 143 try { 144 String packageName = mId.getServiceName().getPackageName(); 145 PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); 146 ApplicationInfo appInfo = packageInfo.applicationInfo; 147 148 // If no custom icon is available, try the icon from the resources 149 if (mIconResourceId != 0) { 150 drawable = packageManager.getDrawable(packageName, mIconResourceId, appInfo); 151 } 152 153 // Fall back to the printer's service's icon if no per printer icon could be found 154 if (drawable == null) { 155 drawable = appInfo.loadIcon(packageManager); 156 } 157 } catch (NameNotFoundException e) { 158 } 159 } 160 161 return drawable; 162 } 163 164 /** 165 * Check if the printer has a custom printer icon. 166 * 167 * @return {@code true} iff the printer has a custom printer icon. 168 * 169 * @hide 170 */ getHasCustomPrinterIcon()171 public boolean getHasCustomPrinterIcon() { 172 return mHasCustomPrinterIcon; 173 } 174 175 /** 176 * Get the printer name. 177 * 178 * @return The printer name. 179 */ getName()180 public @NonNull String getName() { 181 return mName; 182 } 183 184 /** 185 * Gets the printer status. 186 * 187 * @return The status. 188 * 189 * @see #STATUS_BUSY 190 * @see #STATUS_IDLE 191 * @see #STATUS_UNAVAILABLE 192 */ getStatus()193 public @Status int getStatus() { 194 return mStatus; 195 } 196 197 /** 198 * Gets the printer description. 199 * 200 * @return The description. 201 */ getDescription()202 public @Nullable String getDescription() { 203 return mDescription; 204 } 205 206 /** 207 * Get the {@link PendingIntent} that launches the activity showing more information about the 208 * printer. 209 * 210 * @return the {@link PendingIntent} that launches the activity showing more information about 211 * the printer or null if it is not configured 212 * @hide 213 */ getInfoIntent()214 public @Nullable PendingIntent getInfoIntent() { 215 return mInfoIntent; 216 } 217 218 /** 219 * Gets the printer capabilities. 220 * 221 * @return The capabilities. 222 */ getCapabilities()223 public @Nullable PrinterCapabilitiesInfo getCapabilities() { 224 return mCapabilities; 225 } 226 227 /** 228 * Check if printerId is valid. 229 * 230 * @param printerId The printerId that might be valid 231 * @return The valid printerId 232 * @throws IllegalArgumentException if printerId is not valid. 233 */ checkPrinterId(PrinterId printerId)234 private static @NonNull PrinterId checkPrinterId(PrinterId printerId) { 235 return Preconditions.checkNotNull(printerId, "printerId cannot be null."); 236 } 237 238 /** 239 * Check if status is valid. 240 * 241 * @param status The status that might be valid 242 * @return The valid status 243 * @throws IllegalArgumentException if status is not valid. 244 */ checkStatus(int status)245 private static @Status int checkStatus(int status) { 246 if (!(status == STATUS_IDLE 247 || status == STATUS_BUSY 248 || status == STATUS_UNAVAILABLE)) { 249 throw new IllegalArgumentException("status is invalid."); 250 } 251 252 return status; 253 } 254 255 /** 256 * Check if name is valid. 257 * 258 * @param name The name that might be valid 259 * @return The valid name 260 * @throws IllegalArgumentException if name is not valid. 261 */ checkName(String name)262 private static @NonNull String checkName(String name) { 263 return Preconditions.checkStringNotEmpty(name, "name cannot be empty."); 264 } 265 PrinterInfo(Parcel parcel)266 private PrinterInfo(Parcel parcel) { 267 // mName can be null due to unchecked set in Builder.setName and status can be invalid 268 // due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state 269 mId = checkPrinterId((PrinterId) parcel.readParcelable(null)); 270 mName = checkName(parcel.readString()); 271 mStatus = checkStatus(parcel.readInt()); 272 mDescription = parcel.readString(); 273 mCapabilities = parcel.readParcelable(null); 274 mIconResourceId = parcel.readInt(); 275 mHasCustomPrinterIcon = parcel.readByte() != 0; 276 mCustomPrinterIconGen = parcel.readInt(); 277 mInfoIntent = parcel.readParcelable(null); 278 } 279 280 @Override describeContents()281 public int describeContents() { 282 return 0; 283 } 284 285 @Override writeToParcel(Parcel parcel, int flags)286 public void writeToParcel(Parcel parcel, int flags) { 287 parcel.writeParcelable(mId, flags); 288 parcel.writeString(mName); 289 parcel.writeInt(mStatus); 290 parcel.writeString(mDescription); 291 parcel.writeParcelable(mCapabilities, flags); 292 parcel.writeInt(mIconResourceId); 293 parcel.writeByte((byte) (mHasCustomPrinterIcon ? 1 : 0)); 294 parcel.writeInt(mCustomPrinterIconGen); 295 parcel.writeParcelable(mInfoIntent, flags); 296 } 297 298 @Override hashCode()299 public int hashCode() { 300 final int prime = 31; 301 int result = 1; 302 result = prime * result + mId.hashCode(); 303 result = prime * result + mName.hashCode(); 304 result = prime * result + mStatus; 305 result = prime * result + ((mDescription != null) ? mDescription.hashCode() : 0); 306 result = prime * result + ((mCapabilities != null) ? mCapabilities.hashCode() : 0); 307 result = prime * result + mIconResourceId; 308 result = prime * result + (mHasCustomPrinterIcon ? 1 : 0); 309 result = prime * result + mCustomPrinterIconGen; 310 result = prime * result + ((mInfoIntent != null) ? mInfoIntent.hashCode() : 0); 311 return result; 312 } 313 314 /** 315 * Compare two {@link PrinterInfo printerInfos} in all aspects beside being null and the 316 * {@link #mStatus}. 317 * 318 * @param other the other {@link PrinterInfo} 319 * @return true iff the infos are equivalent 320 * @hide 321 */ equalsIgnoringStatus(PrinterInfo other)322 public boolean equalsIgnoringStatus(PrinterInfo other) { 323 if (!mId.equals(other.mId)) { 324 return false; 325 } 326 if (!mName.equals(other.mName)) { 327 return false; 328 } 329 if (!TextUtils.equals(mDescription, other.mDescription)) { 330 return false; 331 } 332 if (mCapabilities == null) { 333 if (other.mCapabilities != null) { 334 return false; 335 } 336 } else if (!mCapabilities.equals(other.mCapabilities)) { 337 return false; 338 } 339 if (mIconResourceId != other.mIconResourceId) { 340 return false; 341 } 342 if (mHasCustomPrinterIcon != other.mHasCustomPrinterIcon) { 343 return false; 344 } 345 if (mCustomPrinterIconGen != other.mCustomPrinterIconGen) { 346 return false; 347 } 348 if (mInfoIntent == null) { 349 if (other.mInfoIntent != null) { 350 return false; 351 } 352 } else if (!mInfoIntent.equals(other.mInfoIntent)) { 353 return false; 354 } 355 return true; 356 } 357 358 @Override equals(Object obj)359 public boolean equals(Object obj) { 360 if (this == obj) { 361 return true; 362 } 363 if (obj == null) { 364 return false; 365 } 366 if (getClass() != obj.getClass()) { 367 return false; 368 } 369 PrinterInfo other = (PrinterInfo) obj; 370 if (!equalsIgnoringStatus(other)) { 371 return false; 372 } 373 if (mStatus != other.mStatus) { 374 return false; 375 } 376 return true; 377 } 378 379 @Override toString()380 public String toString() { 381 StringBuilder builder = new StringBuilder(); 382 builder.append("PrinterInfo{"); 383 builder.append("id=").append(mId); 384 builder.append(", name=").append(mName); 385 builder.append(", status=").append(mStatus); 386 builder.append(", description=").append(mDescription); 387 builder.append(", capabilities=").append(mCapabilities); 388 builder.append(", iconResId=").append(mIconResourceId); 389 builder.append(", hasCustomPrinterIcon=").append(mHasCustomPrinterIcon); 390 builder.append(", customPrinterIconGen=").append(mCustomPrinterIconGen); 391 builder.append(", infoIntent=").append(mInfoIntent); 392 builder.append("\"}"); 393 return builder.toString(); 394 } 395 396 /** 397 * Builder for creating of a {@link PrinterInfo}. 398 */ 399 public static final class Builder { 400 private @NonNull PrinterId mPrinterId; 401 private @NonNull String mName; 402 private @Status int mStatus; 403 private int mIconResourceId; 404 private boolean mHasCustomPrinterIcon; 405 private String mDescription; 406 private PendingIntent mInfoIntent; 407 private PrinterCapabilitiesInfo mCapabilities; 408 private int mCustomPrinterIconGen; 409 410 /** 411 * Constructor. 412 * 413 * @param printerId The printer id. Cannot be null. 414 * @param name The printer name. Cannot be empty. 415 * @param status The printer status. Must be a valid status. 416 * @throws IllegalArgumentException If the printer id is null, or the 417 * printer name is empty or the status is not a valid one. 418 */ Builder(@onNull PrinterId printerId, @NonNull String name, @Status int status)419 public Builder(@NonNull PrinterId printerId, @NonNull String name, @Status int status) { 420 mPrinterId = checkPrinterId(printerId); 421 mName = checkName(name); 422 mStatus = checkStatus(status); 423 } 424 425 /** 426 * Constructor. 427 * 428 * @param other Other info from which to start building. 429 */ Builder(@onNull PrinterInfo other)430 public Builder(@NonNull PrinterInfo other) { 431 mPrinterId = other.mId; 432 mName = other.mName; 433 mStatus = other.mStatus; 434 mIconResourceId = other.mIconResourceId; 435 mHasCustomPrinterIcon = other.mHasCustomPrinterIcon; 436 mDescription = other.mDescription; 437 mInfoIntent = other.mInfoIntent; 438 mCapabilities = other.mCapabilities; 439 mCustomPrinterIconGen = other.mCustomPrinterIconGen; 440 } 441 442 /** 443 * Sets the printer status. 444 * 445 * @param status The status. 446 * @return This builder. 447 * @see PrinterInfo#STATUS_IDLE 448 * @see PrinterInfo#STATUS_BUSY 449 * @see PrinterInfo#STATUS_UNAVAILABLE 450 */ setStatus(@tatus int status)451 public @NonNull Builder setStatus(@Status int status) { 452 mStatus = checkStatus(status); 453 return this; 454 } 455 456 /** 457 * Set a drawable resource as icon for this printer. If no icon is set the printer's 458 * service's icon is used for the printer. 459 * 460 * @param iconResourceId The resource ID of the icon. 461 * @return This builder. 462 * @see PrinterInfo.Builder#setHasCustomPrinterIcon 463 */ setIconResourceId(@rawableRes int iconResourceId)464 public @NonNull Builder setIconResourceId(@DrawableRes int iconResourceId) { 465 mIconResourceId = Preconditions.checkArgumentNonnegative(iconResourceId, 466 "iconResourceId can't be negative"); 467 return this; 468 } 469 470 /** 471 * Declares that the print service can load a custom per printer's icon. If both 472 * {@link PrinterInfo.Builder#setIconResourceId} and a custom icon are set the resource icon 473 * is shown while the custom icon loads but then the custom icon is used. If 474 * {@link PrinterInfo.Builder#setIconResourceId} is not set the printer's service's icon is 475 * shown while loading. 476 * <p> 477 * The icon is requested asynchronously and only when needed via 478 * {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}. 479 * </p> 480 * 481 * @param hasCustomPrinterIcon If the printer has a custom icon or not. 482 * 483 * @return This builder. 484 */ setHasCustomPrinterIcon(boolean hasCustomPrinterIcon)485 public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) { 486 mHasCustomPrinterIcon = hasCustomPrinterIcon; 487 return this; 488 } 489 490 /** 491 * Sets the <strong>localized</strong> printer name which 492 * is shown to the user 493 * 494 * @param name The name. 495 * @return This builder. 496 */ setName(@onNull String name)497 public @NonNull Builder setName(@NonNull String name) { 498 mName = checkName(name); 499 return this; 500 } 501 502 /** 503 * Sets the <strong>localized</strong> printer description 504 * which is shown to the user 505 * 506 * @param description The description. 507 * @return This builder. 508 */ setDescription(@onNull String description)509 public @NonNull Builder setDescription(@NonNull String description) { 510 mDescription = description; 511 return this; 512 } 513 514 /** 515 * Sets the {@link PendingIntent} that launches an activity showing more information about 516 * the printer. 517 * 518 * @param infoIntent The {@link PendingIntent intent}. 519 * @return This builder. 520 */ setInfoIntent(@onNull PendingIntent infoIntent)521 public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) { 522 mInfoIntent = infoIntent; 523 return this; 524 } 525 526 /** 527 * Sets the printer capabilities. 528 * 529 * @param capabilities The capabilities. 530 * @return This builder. 531 */ setCapabilities(@onNull PrinterCapabilitiesInfo capabilities)532 public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) { 533 mCapabilities = capabilities; 534 return this; 535 } 536 537 /** 538 * Creates a new {@link PrinterInfo}. 539 * 540 * @return A new {@link PrinterInfo}. 541 */ build()542 public @NonNull PrinterInfo build() { 543 return new PrinterInfo(mPrinterId, mName, mStatus, mIconResourceId, 544 mHasCustomPrinterIcon, mDescription, mInfoIntent, mCapabilities, 545 mCustomPrinterIconGen); 546 } 547 548 /** 549 * Increments the generation number of the custom printer icon. As the {@link PrinterInfo} 550 * does not match the previous one anymore, users of the {@link PrinterInfo} will reload the 551 * icon if needed. 552 * 553 * @return This builder. 554 * @hide 555 */ incCustomPrinterIconGen()556 public @NonNull Builder incCustomPrinterIconGen() { 557 mCustomPrinterIconGen++; 558 return this; 559 } 560 } 561 562 public static final Parcelable.Creator<PrinterInfo> CREATOR = 563 new Parcelable.Creator<PrinterInfo>() { 564 @Override 565 public PrinterInfo createFromParcel(Parcel parcel) { 566 return new PrinterInfo(parcel); 567 } 568 569 @Override 570 public PrinterInfo[] newArray(int size) { 571 return new PrinterInfo[size]; 572 } 573 }; 574 } 575