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.FloatRange;
20import android.annotation.IntDef;
21import android.annotation.IntRange;
22import android.annotation.NonNull;
23import android.annotation.Nullable;
24import android.annotation.StringRes;
25import android.annotation.TestApi;
26import android.content.pm.PackageManager;
27import android.content.res.Resources;
28import android.os.Bundle;
29import android.os.Parcel;
30import android.os.Parcelable;
31
32import com.android.internal.util.Preconditions;
33
34import java.lang.annotation.Retention;
35import java.lang.annotation.RetentionPolicy;
36import java.util.Arrays;
37
38/**
39 * This class represents the description of a print job. The print job
40 * state includes properties such as its id, print attributes used for
41 * generating the content, and so on. Note that the print jobs state may
42 * change over time and this class represents a snapshot of this state.
43 */
44public final class PrintJobInfo implements Parcelable {
45
46    /** @hide */
47    @IntDef({
48            STATE_CREATED, STATE_QUEUED, STATE_STARTED, STATE_BLOCKED, STATE_COMPLETED,
49            STATE_FAILED, STATE_CANCELED
50    })
51    @Retention(RetentionPolicy.SOURCE)
52    public @interface State {
53    }
54
55    /**
56     * Constant for matching any print job state.
57     *
58     * @hide
59     */
60    public static final int STATE_ANY = -1;
61
62    /**
63     * Constant for matching any print job state.
64     *
65     * @hide
66     */
67    public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2;
68
69    /**
70     * Constant for matching any active print job state.
71     *
72     * @hide
73     */
74    public static final int STATE_ANY_ACTIVE = -3;
75
76    /**
77     * Constant for matching any scheduled, i.e. delivered to a print
78     * service, print job state.
79     *
80     * @hide
81     */
82    public static final int STATE_ANY_SCHEDULED = -4;
83
84    /**
85     * Print job state: The print job is being created but not yet
86     * ready to be printed.
87     * <p>
88     * Next valid states: {@link #STATE_QUEUED}
89     * </p>
90     */
91    public static final int STATE_CREATED = 1;
92
93    /**
94     * Print job state: The print jobs is created, it is ready
95     * to be printed and should be processed.
96     * <p>
97     * Next valid states: {@link #STATE_STARTED}, {@link #STATE_FAILED},
98     * {@link #STATE_CANCELED}
99     * </p>
100     */
101    public static final int STATE_QUEUED = 2;
102
103    /**
104     * Print job state: The print job is being printed.
105     * <p>
106     * Next valid states: {@link #STATE_COMPLETED}, {@link #STATE_FAILED},
107     * {@link #STATE_CANCELED}, {@link #STATE_BLOCKED}
108     * </p>
109     */
110    public static final int STATE_STARTED = 3;
111
112    /**
113     * Print job state: The print job is blocked.
114     * <p>
115     * Next valid states: {@link #STATE_FAILED}, {@link #STATE_CANCELED},
116     * {@link #STATE_STARTED}
117     * </p>
118     */
119    public static final int STATE_BLOCKED = 4;
120
121    /**
122     * Print job state: The print job is successfully printed.
123     * This is a terminal state.
124     * <p>
125     * Next valid states: None
126     * </p>
127     */
128    public static final int STATE_COMPLETED = 5;
129
130    /**
131     * Print job state: The print job was printing but printing failed.
132     * <p>
133     * Next valid states: {@link #STATE_CANCELED}, {@link #STATE_STARTED}
134     * </p>
135     */
136    public static final int STATE_FAILED = 6;
137
138    /**
139     * Print job state: The print job is canceled.
140     * This is a terminal state.
141     * <p>
142     * Next valid states: None
143     * </p>
144     */
145    public static final int STATE_CANCELED = 7;
146
147    /** The unique print job id. */
148    private PrintJobId mId;
149
150    /** The human readable print job label. */
151    private String mLabel;
152
153    /** The unique id of the printer. */
154    private PrinterId mPrinterId;
155
156    /** The name of the printer - internally used */
157    private String mPrinterName;
158
159    /** The state of the print job. */
160    private int mState;
161
162    /** The id of the app that created the job. */
163    private int mAppId;
164
165    /** Optional tag assigned by a print service.*/
166    private String mTag;
167
168    /** The wall time when the print job was created. */
169    private long mCreationTime;
170
171    /** How many copies to print. */
172    private int mCopies;
173
174    /** The pages to print */
175    private PageRange[] mPageRanges;
176
177    /** The print job attributes size. */
178    private PrintAttributes mAttributes;
179
180    /** Information about the printed document. */
181    private PrintDocumentInfo mDocumentInfo;
182
183    /** The progress made on printing this job or -1 if not set. */
184    private float mProgress;
185
186    /** A short string describing the status of this job. */
187    private @Nullable CharSequence mStatus;
188
189    /** A string resource describing the status of this job. */
190    private @StringRes int mStatusRes;
191    private @Nullable CharSequence mStatusResAppPackageName;
192
193    /** Advanced printer specific options. */
194    private Bundle mAdvancedOptions;
195
196    /** Whether we are trying to cancel this print job. */
197    private boolean mCanceling;
198
199    /** @hide*/
200    public PrintJobInfo() {
201        mProgress = -1;
202    }
203
204    /** @hide */
205    public PrintJobInfo(PrintJobInfo other) {
206        mId = other.mId;
207        mLabel = other.mLabel;
208        mPrinterId = other.mPrinterId;
209        mPrinterName = other.mPrinterName;
210        mState = other.mState;
211        mAppId = other.mAppId;
212        mTag = other.mTag;
213        mCreationTime = other.mCreationTime;
214        mCopies = other.mCopies;
215        mPageRanges = other.mPageRanges;
216        mAttributes = other.mAttributes;
217        mDocumentInfo = other.mDocumentInfo;
218        mProgress = other.mProgress;
219        mStatus = other.mStatus;
220        mStatusRes = other.mStatusRes;
221        mStatusResAppPackageName = other.mStatusResAppPackageName;
222        mCanceling = other.mCanceling;
223        mAdvancedOptions = other.mAdvancedOptions;
224    }
225
226    private PrintJobInfo(@NonNull Parcel parcel) {
227        mId = parcel.readParcelable(null);
228        mLabel = parcel.readString();
229        mPrinterId = parcel.readParcelable(null);
230        mPrinterName = parcel.readString();
231        mState = parcel.readInt();
232        mAppId = parcel.readInt();
233        mTag = parcel.readString();
234        mCreationTime = parcel.readLong();
235        mCopies = parcel.readInt();
236        Parcelable[] parcelables = parcel.readParcelableArray(null);
237        if (parcelables != null) {
238            mPageRanges = new PageRange[parcelables.length];
239            for (int i = 0; i < parcelables.length; i++) {
240                mPageRanges[i] = (PageRange) parcelables[i];
241            }
242        }
243        mAttributes = (PrintAttributes) parcel.readParcelable(null);
244        mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
245        mProgress = parcel.readFloat();
246        mStatus = parcel.readCharSequence();
247        mStatusRes = parcel.readInt();
248        mStatusResAppPackageName = parcel.readCharSequence();
249        mCanceling = (parcel.readInt() == 1);
250        mAdvancedOptions = parcel.readBundle();
251
252        if (mAdvancedOptions != null) {
253            Preconditions.checkArgument(!mAdvancedOptions.containsKey(null));
254        }
255    }
256
257    /**
258     * Gets the unique print job id.
259     *
260     * @return The id.
261     */
262    public @Nullable PrintJobId getId() {
263        return mId;
264    }
265
266    /**
267     * Sets the unique print job id.
268     *
269     * @param id The job id.
270     *
271     * @hide
272     */
273    public void setId(@NonNull PrintJobId id) {
274        this.mId = id;
275    }
276
277    /**
278     * Gets the human readable job label.
279     *
280     * @return The label.
281     */
282    public @NonNull String getLabel() {
283        return mLabel;
284    }
285
286    /**
287     * Sets the human readable job label.
288     *
289     * @param label The label.
290     *
291     * @hide
292     */
293    public void setLabel(@NonNull String label) {
294        mLabel = label;
295    }
296
297    /**
298     * Gets the unique target printer id.
299     *
300     * @return The target printer id.
301     */
302    public @Nullable PrinterId getPrinterId() {
303        return mPrinterId;
304    }
305
306    /**
307     * Sets the unique target printer id.
308     *
309     * @param printerId The target printer id.
310     *
311     * @hide
312     */
313    public void setPrinterId(@NonNull PrinterId printerId) {
314        mPrinterId = printerId;
315    }
316
317    /**
318     * Gets the name of the target printer.
319     *
320     * @return The printer name.
321     *
322     * @hide
323     */
324    public @Nullable String getPrinterName() {
325        return mPrinterName;
326    }
327
328    /**
329     * Sets the name of the target printer.
330     *
331     * @param printerName The printer name.
332     *
333     * @hide
334     */
335    public void setPrinterName(@NonNull String printerName) {
336        mPrinterName = printerName;
337    }
338
339    /**
340     * Gets the current job state.
341     *
342     * @return The job state.
343     *
344     * @see #STATE_CREATED
345     * @see #STATE_QUEUED
346     * @see #STATE_STARTED
347     * @see #STATE_COMPLETED
348     * @see #STATE_BLOCKED
349     * @see #STATE_FAILED
350     * @see #STATE_CANCELED
351     */
352    public @State int getState() {
353        return mState;
354    }
355
356    /**
357     * Sets the current job state.
358     *
359     * @param state The job state.
360     *
361     * @hide
362     */
363    public void setState(int state) {
364        mState = state;
365    }
366
367    /**
368     * Sets the progress of the print job.
369     *
370     * @param progress the progress of the job
371     *
372     * @hide
373     */
374    public void setProgress(@FloatRange(from=0.0, to=1.0) float progress) {
375        Preconditions.checkArgumentInRange(progress, 0, 1, "progress");
376
377        mProgress = progress;
378    }
379
380    /**
381     * Sets the status of the print job.
382     *
383     * @param status the status of the job, can be null
384     *
385     * @hide
386     */
387    public void setStatus(@Nullable CharSequence status) {
388        mStatusRes = 0;
389        mStatusResAppPackageName = null;
390
391        mStatus = status;
392    }
393
394    /**
395     * Sets the status of the print job.
396     *
397     * @param status The new status as a string resource
398     * @param appPackageName App package name the resource belongs to
399     *
400     * @hide
401     */
402    public void setStatus(@StringRes int status, @NonNull CharSequence appPackageName) {
403        mStatus = null;
404
405        mStatusRes = status;
406        mStatusResAppPackageName = appPackageName;
407    }
408
409    /**
410     * Sets the owning application id.
411     *
412     * @return The owning app id.
413     *
414     * @hide
415     */
416    public int getAppId() {
417        return mAppId;
418    }
419
420    /**
421     * Sets the owning application id.
422     *
423     * @param appId The owning app id.
424     *
425     * @hide
426     */
427    public void setAppId(int appId) {
428        mAppId = appId;
429    }
430
431    /**
432     * Gets the optional tag assigned by a print service.
433     *
434     * @return The tag.
435     *
436     * @hide
437     */
438    public String getTag() {
439        return mTag;
440    }
441
442    /**
443     * Sets the optional tag assigned by a print service.
444     *
445     * @param tag The tag.
446     *
447     * @hide
448     */
449    public void setTag(String tag) {
450        mTag = tag;
451    }
452
453    /**
454     * Gets the wall time in millisecond when this print job was created.
455     *
456     * @return The creation time in milliseconds.
457     */
458    public long getCreationTime() {
459        return mCreationTime;
460    }
461
462    /**
463     * Sets the wall time in milliseconds when this print job was created.
464     *
465     * @param creationTime The creation time in milliseconds.
466     *
467     * @hide
468     */
469    public void setCreationTime(long creationTime) {
470        if (creationTime < 0) {
471            throw new IllegalArgumentException("creationTime must be non-negative.");
472        }
473        mCreationTime = creationTime;
474    }
475
476    /**
477     * Gets the number of copies.
478     *
479     * @return The number of copies or zero if not set.
480     */
481    public @IntRange(from = 0) int getCopies() {
482        return mCopies;
483    }
484
485    /**
486     * Sets the number of copies.
487     *
488     * @param copyCount The number of copies.
489     *
490     * @hide
491     */
492    public void setCopies(int copyCount) {
493        if (copyCount < 1) {
494            throw new IllegalArgumentException("Copies must be more than one.");
495        }
496        mCopies = copyCount;
497    }
498
499    /**
500     * Gets the included pages.
501     *
502     * @return The included pages or <code>null</code> if not set.
503     */
504    public @Nullable PageRange[] getPages() {
505        return mPageRanges;
506    }
507
508    /**
509     * Sets the included pages.
510     *
511     * @param pageRanges The included pages.
512     *
513     * @hide
514     */
515    public void setPages(PageRange[] pageRanges) {
516        mPageRanges = pageRanges;
517    }
518
519    /**
520     * Gets the print job attributes.
521     *
522     * @return The attributes.
523     */
524    public @NonNull PrintAttributes getAttributes() {
525        return mAttributes;
526    }
527
528    /**
529     * Sets the print job attributes.
530     *
531     * @param attributes The attributes.
532     *
533     * @hide
534     */
535    public void setAttributes(PrintAttributes attributes) {
536        mAttributes = attributes;
537    }
538
539    /**
540     * Gets the info describing the printed document.
541     *
542     * @return The document info.
543     *
544     * @hide
545     */
546    public PrintDocumentInfo getDocumentInfo() {
547        return mDocumentInfo;
548    }
549
550    /**
551     * Sets the info describing the printed document.
552     *
553     * @param info The document info.
554     *
555     * @hide
556     */
557    public void setDocumentInfo(PrintDocumentInfo info) {
558        mDocumentInfo = info;
559    }
560
561    /**
562     * Gets whether this print is being cancelled.
563     *
564     * @return True if the print job is being cancelled.
565     *
566     * @hide
567     */
568    public boolean isCancelling() {
569        return mCanceling;
570    }
571
572    /**
573     * Sets whether this print is being cancelled.
574     *
575     * @param cancelling True if the print job is being cancelled.
576     *
577     * @hide
578     */
579    public void setCancelling(boolean cancelling) {
580        mCanceling = cancelling;
581    }
582
583    /**
584     * Gets whether this job has a given advanced (printer specific) print
585     * option.
586     *
587     * @param key The option key.
588     * @return Whether the option is present.
589     *
590     * @hide
591     */
592    public boolean hasAdvancedOption(String key) {
593        return mAdvancedOptions != null && mAdvancedOptions.containsKey(key);
594    }
595
596    /**
597     * Gets the value of an advanced (printer specific) print option.
598     *
599     * @param key The option key.
600     * @return The option value.
601     *
602     * @hide
603     */
604    public String getAdvancedStringOption(String key) {
605        if (mAdvancedOptions != null) {
606            return mAdvancedOptions.getString(key);
607        }
608        return null;
609    }
610
611    /**
612     * Gets the value of an advanced (printer specific) print option.
613     *
614     * @param key The option key.
615     * @return The option value.
616     *
617     * @hide
618     */
619    public int getAdvancedIntOption(String key) {
620        if (mAdvancedOptions != null) {
621            return mAdvancedOptions.getInt(key);
622        }
623        return 0;
624    }
625
626    /**
627     * Gets the advanced options.
628     *
629     * @return The advanced options.
630     *
631     * @hide
632     */
633    public Bundle getAdvancedOptions() {
634        return mAdvancedOptions;
635    }
636
637    /**
638     * Sets the advanced options.
639     *
640     * @param options The advanced options.
641     *
642     * @hide
643     */
644    public void setAdvancedOptions(Bundle options) {
645        mAdvancedOptions = options;
646    }
647
648    @Override
649    public int describeContents() {
650        return 0;
651    }
652
653    @Override
654    public void writeToParcel(Parcel parcel, int flags) {
655        parcel.writeParcelable(mId, flags);
656        parcel.writeString(mLabel);
657        parcel.writeParcelable(mPrinterId, flags);
658        parcel.writeString(mPrinterName);
659        parcel.writeInt(mState);
660        parcel.writeInt(mAppId);
661        parcel.writeString(mTag);
662        parcel.writeLong(mCreationTime);
663        parcel.writeInt(mCopies);
664        parcel.writeParcelableArray(mPageRanges, flags);
665        parcel.writeParcelable(mAttributes, flags);
666        parcel.writeParcelable(mDocumentInfo, 0);
667        parcel.writeFloat(mProgress);
668        parcel.writeCharSequence(mStatus);
669        parcel.writeInt(mStatusRes);
670        parcel.writeCharSequence(mStatusResAppPackageName);
671        parcel.writeInt(mCanceling ? 1 : 0);
672        parcel.writeBundle(mAdvancedOptions);
673    }
674
675    @Override
676    public String toString() {
677        StringBuilder builder = new StringBuilder();
678        builder.append("PrintJobInfo{");
679        builder.append("label: ").append(mLabel);
680        builder.append(", id: ").append(mId);
681        builder.append(", state: ").append(stateToString(mState));
682        builder.append(", printer: " + mPrinterId);
683        builder.append(", tag: ").append(mTag);
684        builder.append(", creationTime: " + mCreationTime);
685        builder.append(", copies: ").append(mCopies);
686        builder.append(", attributes: " + (mAttributes != null
687                ? mAttributes.toString() : null));
688        builder.append(", documentInfo: " + (mDocumentInfo != null
689                ? mDocumentInfo.toString() : null));
690        builder.append(", cancelling: " + mCanceling);
691        builder.append(", pages: " + (mPageRanges != null
692                ? Arrays.toString(mPageRanges) : null));
693        builder.append(", hasAdvancedOptions: " + (mAdvancedOptions != null));
694        builder.append(", progress: " + mProgress);
695        builder.append(", status: " + (mStatus != null
696                ? mStatus.toString() : null));
697        builder.append(", statusRes: " + mStatusRes);
698        builder.append(", statusResAppPackageName: " + (mStatusResAppPackageName != null
699                ? mStatusResAppPackageName.toString() : null));
700        builder.append("}");
701        return builder.toString();
702    }
703
704    /** @hide */
705    public static String stateToString(int state) {
706        switch (state) {
707            case STATE_CREATED: {
708                return "STATE_CREATED";
709            }
710            case STATE_QUEUED: {
711                return "STATE_QUEUED";
712            }
713            case STATE_STARTED: {
714                return "STATE_STARTED";
715            }
716            case STATE_BLOCKED: {
717                return "STATE_BLOCKED";
718            }
719            case STATE_FAILED: {
720                return "STATE_FAILED";
721            }
722            case STATE_COMPLETED: {
723                return "STATE_COMPLETED";
724            }
725            case STATE_CANCELED: {
726                return "STATE_CANCELED";
727            }
728            default: {
729                return "STATE_UNKNOWN";
730            }
731        }
732    }
733
734    /**
735     * Get the progress that has been made printing this job.
736     *
737     * @return the print progress or -1 if not set
738     * @hide
739     */
740    @TestApi
741    public float getProgress() {
742        return mProgress;
743    }
744
745    /**
746     * Get the status of this job.
747     *
748     * @param pm Package manager used to resolve the string
749     *
750     * @return the status of this job or null if not set
751     * @hide
752     */
753    @TestApi
754    public @Nullable CharSequence getStatus(@NonNull PackageManager pm) {
755        if (mStatusRes == 0) {
756            return mStatus;
757        } else {
758            try {
759                return pm.getResourcesForApplication(mStatusResAppPackageName.toString())
760                        .getString(mStatusRes);
761            } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
762                return null;
763            }
764        }
765    }
766
767    /**
768     * Builder for creating a {@link PrintJobInfo}.
769     */
770    public static final class Builder {
771        private final PrintJobInfo mPrototype;
772
773        /**
774         * Constructor.
775         *
776         * @param prototype Prototype to use as a starting point.
777         * Can be <code>null</code>.
778         */
779        public Builder(@Nullable PrintJobInfo prototype) {
780            mPrototype = (prototype != null)
781                    ? new PrintJobInfo(prototype)
782                    : new PrintJobInfo();
783        }
784
785        /**
786         * Sets the number of copies.
787         *
788         * @param copies The number of copies.
789         */
790        public void setCopies(@IntRange(from = 1) int copies) {
791            mPrototype.mCopies = copies;
792        }
793
794        /**
795         * Sets the print job attributes.
796         *
797         * @param attributes The attributes.
798         */
799        public void setAttributes(@NonNull PrintAttributes attributes) {
800            mPrototype.mAttributes = attributes;
801        }
802
803        /**
804         * Sets the included pages.
805         *
806         * @param pages The included pages.
807         */
808        public void setPages(@NonNull PageRange[] pages) {
809            mPrototype.mPageRanges = pages;
810        }
811
812        /**
813         * Sets the progress of the print job.
814         *
815         * @param progress the progress of the job
816         * @hide
817         */
818        public void setProgress(@FloatRange(from=0.0, to=1.0) float progress) {
819            Preconditions.checkArgumentInRange(progress, 0, 1, "progress");
820
821            mPrototype.mProgress = progress;
822        }
823
824        /**
825         * Sets the status of the print job.
826         *
827         * @param status the status of the job, can be null
828         * @hide
829         */
830        public void setStatus(@Nullable CharSequence status) {
831            mPrototype.mStatus = status;
832        }
833
834        /**
835         * Puts an advanced (printer specific) option.
836         *
837         * @param key The option key.
838         * @param value The option value.
839         */
840        public void putAdvancedOption(@NonNull String key, @Nullable String value) {
841            Preconditions.checkNotNull(key, "key cannot be null");
842
843            if (mPrototype.mAdvancedOptions == null) {
844                mPrototype.mAdvancedOptions = new Bundle();
845            }
846            mPrototype.mAdvancedOptions.putString(key, value);
847        }
848
849        /**
850         * Puts an advanced (printer specific) option.
851         *
852         * @param key The option key.
853         * @param value The option value.
854         */
855        public void putAdvancedOption(@NonNull String key, int value) {
856            if (mPrototype.mAdvancedOptions == null) {
857                mPrototype.mAdvancedOptions = new Bundle();
858            }
859            mPrototype.mAdvancedOptions.putInt(key, value);
860        }
861
862        /**
863         * Creates a new {@link PrintJobInfo} instance.
864         *
865         * @return The new instance.
866         */
867        public @NonNull PrintJobInfo build() {
868            return mPrototype;
869        }
870    }
871
872    public static final Parcelable.Creator<PrintJobInfo> CREATOR =
873            new Creator<PrintJobInfo>() {
874        @Override
875        public PrintJobInfo createFromParcel(Parcel parcel) {
876            return new PrintJobInfo(parcel);
877        }
878
879        @Override
880        public PrintJobInfo[] newArray(int size) {
881            return new PrintJobInfo[size];
882        }
883    };
884}
885