• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      * Get the printer name.
166      *
167      * @return The printer name.
168      */
getName()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      */
getStatus()182     public @Status int getStatus() {
183         return mStatus;
184     }
185 
186     /**
187      * Gets the  printer description.
188      *
189      * @return The description.
190      */
getDescription()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      */
getInfoIntent()203     public @Nullable PendingIntent getInfoIntent() {
204         return mInfoIntent;
205     }
206 
207     /**
208      * Gets the printer capabilities.
209      *
210      * @return The capabilities.
211      */
getCapabilities()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      */
checkPrinterId(PrinterId printerId)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      */
checkStatus(int status)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      */
checkName(String name)251     private static @NonNull String checkName(String name) {
252         return Preconditions.checkStringNotEmpty(name, "name cannot be empty.");
253     }
254 
PrinterInfo(Parcel parcel)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
describeContents()270     public int describeContents() {
271         return 0;
272     }
273 
274     @Override
writeToParcel(Parcel parcel, int flags)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
hashCode()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      */
equalsIgnoringStatus(PrinterInfo other)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
equals(Object obj)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
toString()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          */
Builder(@onNull PrinterId printerId, @NonNull String name, @Status int status)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          */
Builder(@onNull PrinterInfo other)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          */
setStatus(@tatus int status)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          */
setIconResourceId(@rawableRes int iconResourceId)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          */
setHasCustomPrinterIcon(boolean hasCustomPrinterIcon)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          */
setName(@onNull String name)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          */
setDescription(@onNull String description)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          */
setInfoIntent(@onNull PendingIntent infoIntent)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          */
setCapabilities(@onNull PrinterCapabilitiesInfo capabilities)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          */
build()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          */
incCustomPrinterIconGen()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