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.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 
     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      */
    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
    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      * Get the printer name.
    166      *
    167      * @return The printer name.
    168      */
    169     public @NonNull String getName() {
    170         return mName;
    171     }
    172 
    173     /**
    174      * Gets the printer status.
    175      *
    176      * @return The status.
    177      *
    178      * @see #STATUS_BUSY
    179      * @see #STATUS_IDLE
    180      * @see #STATUS_UNAVAILABLE
    181      */
    182     public @Status int getStatus() {
    183         return mStatus;
    184     }
    185 
    186     /**
    187      * Gets the  printer description.
    188      *
    189      * @return The description.
    190      */
    191     public @Nullable String getDescription() {
    192         return mDescription;
    193     }
    194 
    195     /**
    196      * Get the {@link PendingIntent} that launches the activity showing more information about the
    197      * printer.
    198      *
    199      * @return the {@link PendingIntent} that launches the activity showing more information about
    200      *         the printer or null if it is not configured
    201      * @hide
    202      */
    203     public @Nullable PendingIntent getInfoIntent() {
    204         return mInfoIntent;
    205     }
    206 
    207     /**
    208      * Gets the printer capabilities.
    209      *
    210      * @return The capabilities.
    211      */
    212     public @Nullable PrinterCapabilitiesInfo getCapabilities() {
    213         return mCapabilities;
    214     }
    215 
    216     /**
    217      * Check if printerId is valid.
    218      *
    219      * @param printerId The printerId that might be valid
    220      * @return The valid printerId
    221      * @throws IllegalArgumentException if printerId is not valid.
    222      */
    223     private static @NonNull PrinterId checkPrinterId(PrinterId printerId) {
    224         return Preconditions.checkNotNull(printerId, "printerId cannot be null.");
    225     }
    226 
    227     /**
    228      * Check if status is valid.
    229      *
    230      * @param status The status that might be valid
    231      * @return The valid status
    232      * @throws IllegalArgumentException if status is not valid.
    233      */
    234     private static @Status int checkStatus(int status) {
    235         if (!(status == STATUS_IDLE
    236                 || status == STATUS_BUSY
    237                 || status == STATUS_UNAVAILABLE)) {
    238             throw new IllegalArgumentException("status is invalid.");
    239         }
    240 
    241         return status;
    242     }
    243 
    244     /**
    245      * Check if name is valid.
    246      *
    247      * @param name The name that might be valid
    248      * @return The valid name
    249      * @throws IllegalArgumentException if name is not valid.
    250      */
    251     private static @NonNull String checkName(String name) {
    252         return Preconditions.checkStringNotEmpty(name, "name cannot be empty.");
    253     }
    254 
    255     private PrinterInfo(Parcel parcel) {
    256         // mName can be null due to unchecked set in Builder.setName and status can be invalid
    257         // due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state
    258         mId = checkPrinterId((PrinterId) parcel.readParcelable(null));
    259         mName = checkName(parcel.readString());
    260         mStatus = checkStatus(parcel.readInt());
    261         mDescription = parcel.readString();
    262         mCapabilities = parcel.readParcelable(null);
    263         mIconResourceId = parcel.readInt();
    264         mHasCustomPrinterIcon = parcel.readByte() != 0;
    265         mCustomPrinterIconGen = parcel.readInt();
    266         mInfoIntent = parcel.readParcelable(null);
    267     }
    268 
    269     @Override
    270     public int describeContents() {
    271         return 0;
    272     }
    273 
    274     @Override
    275     public void writeToParcel(Parcel parcel, int flags) {
    276         parcel.writeParcelable(mId, flags);
    277         parcel.writeString(mName);
    278         parcel.writeInt(mStatus);
    279         parcel.writeString(mDescription);
    280         parcel.writeParcelable(mCapabilities, flags);
    281         parcel.writeInt(mIconResourceId);
    282         parcel.writeByte((byte) (mHasCustomPrinterIcon ? 1 : 0));
    283         parcel.writeInt(mCustomPrinterIconGen);
    284         parcel.writeParcelable(mInfoIntent, flags);
    285     }
    286 
    287     @Override
    288     public int hashCode() {
    289         final int prime = 31;
    290         int result = 1;
    291         result = prime * result + mId.hashCode();
    292         result = prime * result + mName.hashCode();
    293         result = prime * result + mStatus;
    294         result = prime * result + ((mDescription != null) ? mDescription.hashCode() : 0);
    295         result = prime * result + ((mCapabilities != null) ? mCapabilities.hashCode() : 0);
    296         result = prime * result + mIconResourceId;
    297         result = prime * result + (mHasCustomPrinterIcon ? 1 : 0);
    298         result = prime * result + mCustomPrinterIconGen;
    299         result = prime * result + ((mInfoIntent != null) ? mInfoIntent.hashCode() : 0);
    300         return result;
    301     }
    302 
    303     /**
    304      * Compare two {@link PrinterInfo printerInfos} in all aspects beside being null and the
    305      * {@link #mStatus}.
    306      *
    307      * @param other the other {@link PrinterInfo}
    308      * @return true iff the infos are equivalent
    309      * @hide
    310      */
    311     public boolean equalsIgnoringStatus(PrinterInfo other) {
    312         if (!mId.equals(other.mId)) {
    313             return false;
    314         }
    315         if (!mName.equals(other.mName)) {
    316            return false;
    317         }
    318         if (!TextUtils.equals(mDescription, other.mDescription)) {
    319             return false;
    320         }
    321         if (mCapabilities == null) {
    322             if (other.mCapabilities != null) {
    323                 return false;
    324             }
    325         } else if (!mCapabilities.equals(other.mCapabilities)) {
    326             return false;
    327         }
    328         if (mIconResourceId != other.mIconResourceId) {
    329             return false;
    330         }
    331         if (mHasCustomPrinterIcon != other.mHasCustomPrinterIcon) {
    332             return false;
    333         }
    334         if (mCustomPrinterIconGen != other.mCustomPrinterIconGen) {
    335             return false;
    336         }
    337         if (mInfoIntent == null) {
    338             if (other.mInfoIntent != null) {
    339                 return false;
    340             }
    341         } else if (!mInfoIntent.equals(other.mInfoIntent)) {
    342             return false;
    343         }
    344         return true;
    345     }
    346 
    347     @Override
    348     public boolean equals(Object obj) {
    349         if (this == obj) {
    350             return true;
    351         }
    352         if (obj == null) {
    353             return false;
    354         }
    355         if (getClass() != obj.getClass()) {
    356             return false;
    357         }
    358         PrinterInfo other = (PrinterInfo) obj;
    359         if (!equalsIgnoringStatus(other)) {
    360             return false;
    361         }
    362         if (mStatus != other.mStatus) {
    363             return false;
    364         }
    365         return true;
    366     }
    367 
    368     @Override
    369     public String toString() {
    370         StringBuilder builder = new StringBuilder();
    371         builder.append("PrinterInfo{");
    372         builder.append("id=").append(mId);
    373         builder.append(", name=").append(mName);
    374         builder.append(", status=").append(mStatus);
    375         builder.append(", description=").append(mDescription);
    376         builder.append(", capabilities=").append(mCapabilities);
    377         builder.append(", iconResId=").append(mIconResourceId);
    378         builder.append(", hasCustomPrinterIcon=").append(mHasCustomPrinterIcon);
    379         builder.append(", customPrinterIconGen=").append(mCustomPrinterIconGen);
    380         builder.append(", infoIntent=").append(mInfoIntent);
    381         builder.append("\"}");
    382         return builder.toString();
    383     }
    384 
    385     /**
    386      * Builder for creating of a {@link PrinterInfo}.
    387      */
    388     public static final class Builder {
    389         private @NonNull PrinterId mPrinterId;
    390         private @NonNull String mName;
    391         private @Status int mStatus;
    392         private int mIconResourceId;
    393         private boolean mHasCustomPrinterIcon;
    394         private String mDescription;
    395         private PendingIntent mInfoIntent;
    396         private PrinterCapabilitiesInfo mCapabilities;
    397         private int mCustomPrinterIconGen;
    398 
    399         /**
    400          * Constructor.
    401          *
    402          * @param printerId The printer id. Cannot be null.
    403          * @param name The printer name. Cannot be empty.
    404          * @param status The printer status. Must be a valid status.
    405          * @throws IllegalArgumentException If the printer id is null, or the
    406          * printer name is empty or the status is not a valid one.
    407          */
    408         public Builder(@NonNull PrinterId printerId, @NonNull String name, @Status int status) {
    409             mPrinterId = checkPrinterId(printerId);
    410             mName = checkName(name);
    411             mStatus = checkStatus(status);
    412         }
    413 
    414         /**
    415          * Constructor.
    416          *
    417          * @param other Other info from which to start building.
    418          */
    419         public Builder(@NonNull PrinterInfo other) {
    420             mPrinterId = other.mId;
    421             mName = other.mName;
    422             mStatus = other.mStatus;
    423             mIconResourceId = other.mIconResourceId;
    424             mHasCustomPrinterIcon = other.mHasCustomPrinterIcon;
    425             mDescription = other.mDescription;
    426             mInfoIntent = other.mInfoIntent;
    427             mCapabilities = other.mCapabilities;
    428             mCustomPrinterIconGen = other.mCustomPrinterIconGen;
    429         }
    430 
    431         /**
    432          * Sets the printer status.
    433          *
    434          * @param status The status.
    435          * @return This builder.
    436          * @see PrinterInfo#STATUS_IDLE
    437          * @see PrinterInfo#STATUS_BUSY
    438          * @see PrinterInfo#STATUS_UNAVAILABLE
    439          */
    440         public @NonNull Builder setStatus(@Status int status) {
    441             mStatus = checkStatus(status);
    442             return this;
    443         }
    444 
    445         /**
    446          * Set a drawable resource as icon for this printer. If no icon is set the printer's
    447          * service's icon is used for the printer.
    448          *
    449          * @param iconResourceId The resource ID of the icon.
    450          * @return This builder.
    451          * @see PrinterInfo.Builder#setHasCustomPrinterIcon
    452          */
    453         public @NonNull Builder setIconResourceId(@DrawableRes int iconResourceId) {
    454             mIconResourceId = Preconditions.checkArgumentNonnegative(iconResourceId,
    455                     "iconResourceId can't be negative");
    456             return this;
    457         }
    458 
    459         /**
    460          * Declares that the print service can load a custom per printer's icon. If both
    461          * {@link PrinterInfo.Builder#setIconResourceId} and a custom icon are set the resource icon
    462          * is shown while the custom icon loads but then the custom icon is used. If
    463          * {@link PrinterInfo.Builder#setIconResourceId} is not set the printer's service's icon is
    464          * shown while loading.
    465          * <p>
    466          * The icon is requested asynchronously and only when needed via
    467          * {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}.
    468          * </p>
    469          *
    470          * @param hasCustomPrinterIcon If the printer has a custom icon or not.
    471          *
    472          * @return This builder.
    473          */
    474         public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) {
    475             mHasCustomPrinterIcon = hasCustomPrinterIcon;
    476             return this;
    477         }
    478 
    479         /**
    480          * Sets the <strong>localized</strong> printer name which
    481          * is shown to the user
    482          *
    483          * @param name The name.
    484          * @return This builder.
    485          */
    486         public @NonNull Builder setName(@NonNull String name) {
    487             mName = checkName(name);
    488             return this;
    489         }
    490 
    491         /**
    492          * Sets the <strong>localized</strong> printer description
    493          * which is shown to the user
    494          *
    495          * @param description The description.
    496          * @return This builder.
    497          */
    498         public @NonNull Builder setDescription(@NonNull String description) {
    499             mDescription = description;
    500             return this;
    501         }
    502 
    503         /**
    504          * Sets the {@link PendingIntent} that launches an activity showing more information about
    505          * the printer.
    506          *
    507          * @param infoIntent The {@link PendingIntent intent}.
    508          * @return This builder.
    509          */
    510         public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) {
    511             mInfoIntent = infoIntent;
    512             return this;
    513         }
    514 
    515         /**
    516          * Sets the printer capabilities.
    517          *
    518          * @param capabilities The capabilities.
    519          * @return This builder.
    520          */
    521         public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) {
    522             mCapabilities = capabilities;
    523             return this;
    524         }
    525 
    526         /**
    527          * Creates a new {@link PrinterInfo}.
    528          *
    529          * @return A new {@link PrinterInfo}.
    530          */
    531         public @NonNull PrinterInfo build() {
    532             return new PrinterInfo(mPrinterId, mName, mStatus, mIconResourceId,
    533                     mHasCustomPrinterIcon, mDescription, mInfoIntent, mCapabilities,
    534                     mCustomPrinterIconGen);
    535         }
    536 
    537         /**
    538          * Increments the generation number of the custom printer icon. As the {@link PrinterInfo}
    539          * does not match the previous one anymore, users of the {@link PrinterInfo} will reload the
    540          * icon if needed.
    541          *
    542          * @return This builder.
    543          * @hide
    544          */
    545         public @NonNull Builder incCustomPrinterIconGen() {
    546             mCustomPrinterIconGen++;
    547             return this;
    548         }
    549     }
    550 
    551     public static final Parcelable.Creator<PrinterInfo> CREATOR =
    552             new Parcelable.Creator<PrinterInfo>() {
    553         @Override
    554         public PrinterInfo createFromParcel(Parcel parcel) {
    555             return new PrinterInfo(parcel);
    556         }
    557 
    558         @Override
    559         public PrinterInfo[] newArray(int size) {
    560             return new PrinterInfo[size];
    561         }
    562     };
    563 }
    564