PrinterInfo.java revision c2ad22663ba8cbd0ceb35e760c5f3c4084fb5033
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 com.android.internal.util.Preconditions;
37
38import java.lang.annotation.Retention;
39import 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 */
52public 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         * @return This builder.
471         */
472        public @NonNull Builder setHasCustomPrinterIcon() {
473            mHasCustomPrinterIcon = true;
474            return this;
475        }
476
477        /**
478         * Sets the <strong>localized</strong> printer name which
479         * is shown to the user
480         *
481         * @param name The name.
482         * @return This builder.
483         */
484        public @NonNull Builder setName(@NonNull String name) {
485            mName = checkName(name);
486            return this;
487        }
488
489        /**
490         * Sets the <strong>localized</strong> printer description
491         * which is shown to the user
492         *
493         * @param description The description.
494         * @return This builder.
495         */
496        public @NonNull Builder setDescription(@NonNull String description) {
497            mDescription = description;
498            return this;
499        }
500
501        /**
502         * Sets the {@link PendingIntent} that launches an activity showing more information about
503         * the printer.
504         *
505         * @param infoIntent The {@link PendingIntent intent}.
506         * @return This builder.
507         */
508        public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) {
509            mInfoIntent = infoIntent;
510            return this;
511        }
512
513        /**
514         * Sets the printer capabilities.
515         *
516         * @param capabilities The capabilities.
517         * @return This builder.
518         */
519        public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) {
520            mCapabilities = capabilities;
521            return this;
522        }
523
524        /**
525         * Creates a new {@link PrinterInfo}.
526         *
527         * @return A new {@link PrinterInfo}.
528         */
529        public @NonNull PrinterInfo build() {
530            return new PrinterInfo(mPrinterId, mName, mStatus, mIconResourceId,
531                    mHasCustomPrinterIcon, mDescription, mInfoIntent, mCapabilities,
532                    mCustomPrinterIconGen);
533        }
534
535        /**
536         * Increments the generation number of the custom printer icon. As the {@link PrinterInfo}
537         * does not match the previous one anymore, users of the {@link PrinterInfo} will reload the
538         * icon if needed.
539         *
540         * @return This builder.
541         * @hide
542         */
543        public @NonNull Builder incCustomPrinterIconGen() {
544            mCustomPrinterIconGen++;
545            return this;
546        }
547    }
548
549    public static final Parcelable.Creator<PrinterInfo> CREATOR =
550            new Parcelable.Creator<PrinterInfo>() {
551        @Override
552        public PrinterInfo createFromParcel(Parcel parcel) {
553            return new PrinterInfo(parcel);
554        }
555
556        @Override
557        public PrinterInfo[] newArray(int size) {
558            return new PrinterInfo[size];
559        }
560    };
561}
562