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