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.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 
     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      */
    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
    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      */
    175     public boolean getHasCustomPrinterIcon() {
    176         return mHasCustomPrinterIcon;
    177     }
    178 
    179     /**
    180      * Get the printer name.
    181      *
    182      * @return The printer name.
    183      */
    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      */
    197     public @Status int getStatus() {
    198         return mStatus;
    199     }
    200 
    201     /**
    202      * Gets the  printer description.
    203      *
    204      * @return The description.
    205      */
    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      */
    218     public @Nullable PendingIntent getInfoIntent() {
    219         return mInfoIntent;
    220     }
    221 
    222     /**
    223      * Gets the printer capabilities.
    224      *
    225      * @return The capabilities.
    226      */
    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      */
    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      */
    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      */
    266     private static @NonNull String checkName(String name) {
    267         return Preconditions.checkStringNotEmpty(name, "name cannot be empty.");
    268     }
    269 
    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
    285     public int describeContents() {
    286         return 0;
    287     }
    288 
    289     @Override
    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
    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      */
    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
    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
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    560         public @NonNull Builder incCustomPrinterIconGen() {
    561             mCustomPrinterIconGen++;
    562             return this;
    563         }
    564     }
    565 
    566     public static final 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