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