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;
31import android.service.print.PrintJobInfoProto;
32
33import com.android.internal.util.Preconditions;
34
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37import java.util.Arrays;
38
39/**
40 * This class represents the description of a print job. The print job
41 * state includes properties such as its id, print attributes used for
42 * generating the content, and so on. Note that the print jobs state may
43 * change over time and this class represents a snapshot of this state.
44 */
45public final class PrintJobInfo implements Parcelable {
46
47    /** @hide */
48    @IntDef(prefix = { "STATE_" }, value = {
49            STATE_CREATED,
50            STATE_QUEUED,
51            STATE_STARTED,
52            STATE_BLOCKED,
53            STATE_COMPLETED,
54            STATE_FAILED,
55            STATE_CANCELED
56    })
57    @Retention(RetentionPolicy.SOURCE)
58    public @interface State {
59    }
60
61    /**
62     * Constant for matching any print job state.
63     *
64     * @hide
65     */
66    public static final int STATE_ANY = -1;
67
68    /**
69     * Constant for matching any print job state.
70     *
71     * @hide
72     */
73    public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2;
74
75    /**
76     * Constant for matching any active print job state.
77     *
78     * @hide
79     */
80    public static final int STATE_ANY_ACTIVE = -3;
81
82    /**
83     * Constant for matching any scheduled, i.e. delivered to a print
84     * service, print job state.
85     *
86     * @hide
87     */
88    public static final int STATE_ANY_SCHEDULED = -4;
89
90    /**
91     * Print job state: The print job is being created but not yet
92     * ready to be printed.
93     * <p>
94     * Next valid states: {@link #STATE_QUEUED}
95     * </p>
96     */
97    public static final int STATE_CREATED = PrintJobInfoProto.STATE_CREATED;
98
99    /**
100     * Print job state: The print jobs is created, it is ready
101     * to be printed and should be processed.
102     * <p>
103     * Next valid states: {@link #STATE_STARTED}, {@link #STATE_FAILED},
104     * {@link #STATE_CANCELED}
105     * </p>
106     */
107    public static final int STATE_QUEUED = PrintJobInfoProto.STATE_QUEUED;
108
109    /**
110     * Print job state: The print job is being printed.
111     * <p>
112     * Next valid states: {@link #STATE_COMPLETED}, {@link #STATE_FAILED},
113     * {@link #STATE_CANCELED}, {@link #STATE_BLOCKED}
114     * </p>
115     */
116    public static final int STATE_STARTED = PrintJobInfoProto.STATE_STARTED;
117
118    /**
119     * Print job state: The print job is blocked.
120     * <p>
121     * Next valid states: {@link #STATE_FAILED}, {@link #STATE_CANCELED},
122     * {@link #STATE_STARTED}
123     * </p>
124     */
125    public static final int STATE_BLOCKED = PrintJobInfoProto.STATE_BLOCKED;
126
127    /**
128     * Print job state: The print job is successfully printed.
129     * This is a terminal state.
130     * <p>
131     * Next valid states: None
132     * </p>
133     */
134    public static final int STATE_COMPLETED = PrintJobInfoProto.STATE_COMPLETED;
135
136    /**
137     * Print job state: The print job was printing but printing failed.
138     * <p>
139     * Next valid states: {@link #STATE_CANCELED}, {@link #STATE_STARTED}
140     * </p>
141     */
142    public static final int STATE_FAILED = PrintJobInfoProto.STATE_FAILED;
143
144    /**
145     * Print job state: The print job is canceled.
146     * This is a terminal state.
147     * <p>
148     * Next valid states: None
149     * </p>
150     */
151    public static final int STATE_CANCELED = PrintJobInfoProto.STATE_CANCELED;
152
153    /** The unique print job id. */
154    private PrintJobId mId;
155
156    /** The human readable print job label. */
157    private String mLabel;
158
159    /** The unique id of the printer. */
160    private PrinterId mPrinterId;
161
162    /** The name of the printer - internally used */
163    private String mPrinterName;
164
165    /** The state of the print job. */
166    private int mState;
167
168    /** The id of the app that created the job. */
169    private int mAppId;
170
171    /** Optional tag assigned by a print service.*/
172    private String mTag;
173
174    /** The wall time when the print job was created. */
175    private long mCreationTime;
176
177    /** How many copies to print. */
178    private int mCopies;
179
180    /** The pages to print */
181    private PageRange[] mPageRanges;
182
183    /** The print job attributes size. */
184    private PrintAttributes mAttributes;
185
186    /** Information about the printed document. */
187    private PrintDocumentInfo mDocumentInfo;
188
189    /** The progress made on printing this job or -1 if not set. */
190    private float mProgress;
191
192    /** A short string describing the status of this job. */
193    private @Nullable CharSequence mStatus;
194
195    /** A string resource describing the status of this job. */
196    private @StringRes int mStatusRes;
197    private @Nullable CharSequence mStatusResAppPackageName;
198
199    /** Advanced printer specific options. */
200    private Bundle mAdvancedOptions;
201
202    /** Whether we are trying to cancel this print job. */
203    private boolean mCanceling;
204
205    /** @hide*/
206    public PrintJobInfo() {
207        mProgress = -1;
208    }
209
210    /** @hide */
211    public PrintJobInfo(PrintJobInfo other) {
212        mId = other.mId;
213        mLabel = other.mLabel;
214        mPrinterId = other.mPrinterId;
215        mPrinterName = other.mPrinterName;
216        mState = other.mState;
217        mAppId = other.mAppId;
218        mTag = other.mTag;
219        mCreationTime = other.mCreationTime;
220        mCopies = other.mCopies;
221        mPageRanges = other.mPageRanges;
222        mAttributes = other.mAttributes;
223        mDocumentInfo = other.mDocumentInfo;
224        mProgress = other.mProgress;
225        mStatus = other.mStatus;
226        mStatusRes = other.mStatusRes;
227        mStatusResAppPackageName = other.mStatusResAppPackageName;
228        mCanceling = other.mCanceling;
229        mAdvancedOptions = other.mAdvancedOptions;
230    }
231
232    private PrintJobInfo(@NonNull Parcel parcel) {
233        mId = parcel.readParcelable(null);
234        mLabel = parcel.readString();
235        mPrinterId = parcel.readParcelable(null);
236        mPrinterName = parcel.readString();
237        mState = parcel.readInt();
238        mAppId = parcel.readInt();
239        mTag = parcel.readString();
240        mCreationTime = parcel.readLong();
241        mCopies = parcel.readInt();
242        Parcelable[] parcelables = parcel.readParcelableArray(null);
243        if (parcelables != null) {
244            mPageRanges = new PageRange[parcelables.length];
245            for (int i = 0; i < parcelables.length; i++) {
246                mPageRanges[i] = (PageRange) parcelables[i];
247            }
248        }
249        mAttributes = (PrintAttributes) parcel.readParcelable(null);
250        mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
251        mProgress = parcel.readFloat();
252        mStatus = parcel.readCharSequence();
253        mStatusRes = parcel.readInt();
254        mStatusResAppPackageName = parcel.readCharSequence();
255        mCanceling = (parcel.readInt() == 1);
256        mAdvancedOptions = parcel.readBundle();
257
258        if (mAdvancedOptions != null) {
259            Preconditions.checkArgument(!mAdvancedOptions.containsKey(null));
260        }
261    }
262
263    /**
264     * Gets the unique print job id.
265     *
266     * @return The id.
267     */
268    public @Nullable PrintJobId getId() {
269        return mId;
270    }
271
272    /**
273     * Sets the unique print job id.
274     *
275     * @param id The job id.
276     *
277     * @hide
278     */
279    public void setId(@NonNull PrintJobId id) {
280        this.mId = id;
281    }
282
283    /**
284     * Gets the human readable job label.
285     *
286     * @return The label.
287     */
288    public @NonNull String getLabel() {
289        return mLabel;
290    }
291
292    /**
293     * Sets the human readable job label.
294     *
295     * @param label The label.
296     *
297     * @hide
298     */
299    public void setLabel(@NonNull String label) {
300        mLabel = label;
301    }
302
303    /**
304     * Gets the unique target printer id.
305     *
306     * @return The target printer id.
307     */
308    public @Nullable PrinterId getPrinterId() {
309        return mPrinterId;
310    }
311
312    /**
313     * Sets the unique target printer id.
314     *
315     * @param printerId The target printer id.
316     *
317     * @hide
318     */
319    public void setPrinterId(@NonNull PrinterId printerId) {
320        mPrinterId = printerId;
321    }
322
323    /**
324     * Gets the name of the target printer.
325     *
326     * @return The printer name.
327     *
328     * @hide
329     */
330    public @Nullable String getPrinterName() {
331        return mPrinterName;
332    }
333
334    /**
335     * Sets the name of the target printer.
336     *
337     * @param printerName The printer name.
338     *
339     * @hide
340     */
341    public void setPrinterName(@NonNull String printerName) {
342        mPrinterName = printerName;
343    }
344
345    /**
346     * Gets the current job state.
347     *
348     * @return The job state.
349     *
350     * @see #STATE_CREATED
351     * @see #STATE_QUEUED
352     * @see #STATE_STARTED
353     * @see #STATE_COMPLETED
354     * @see #STATE_BLOCKED
355     * @see #STATE_FAILED
356     * @see #STATE_CANCELED
357     */
358    public @State int getState() {
359        return mState;
360    }
361
362    /**
363     * Sets the current job state.
364     *
365     * @param state The job state.
366     *
367     * @hide
368     */
369    public void setState(int state) {
370        mState = state;
371    }
372
373    /**
374     * Sets the progress of the print job.
375     *
376     * @param progress the progress of the job
377     *
378     * @hide
379     */
380    public void setProgress(@FloatRange(from=0.0, to=1.0) float progress) {
381        Preconditions.checkArgumentInRange(progress, 0, 1, "progress");
382
383        mProgress = progress;
384    }
385
386    /**
387     * Sets the status of the print job.
388     *
389     * @param status the status of the job, can be null
390     *
391     * @hide
392     */
393    public void setStatus(@Nullable CharSequence status) {
394        mStatusRes = 0;
395        mStatusResAppPackageName = null;
396
397        mStatus = status;
398    }
399
400    /**
401     * Sets the status of the print job.
402     *
403     * @param status The new status as a string resource
404     * @param appPackageName App package name the resource belongs to
405     *
406     * @hide
407     */
408    public void setStatus(@StringRes int status, @NonNull CharSequence appPackageName) {
409        mStatus = null;
410
411        mStatusRes = status;
412        mStatusResAppPackageName = appPackageName;
413    }
414
415    /**
416     * Sets the owning application id.
417     *
418     * @return The owning app id.
419     *
420     * @hide
421     */
422    public int getAppId() {
423        return mAppId;
424    }
425
426    /**
427     * Sets the owning application id.
428     *
429     * @param appId The owning app id.
430     *
431     * @hide
432     */
433    public void setAppId(int appId) {
434        mAppId = appId;
435    }
436
437    /**
438     * Gets the optional tag assigned by a print service.
439     *
440     * @return The tag.
441     *
442     * @hide
443     */
444    public String getTag() {
445        return mTag;
446    }
447
448    /**
449     * Sets the optional tag assigned by a print service.
450     *
451     * @param tag The tag.
452     *
453     * @hide
454     */
455    public void setTag(String tag) {
456        mTag = tag;
457    }
458
459    /**
460     * Gets the wall time in millisecond when this print job was created.
461     *
462     * @return The creation time in milliseconds.
463     */
464    public long getCreationTime() {
465        return mCreationTime;
466    }
467
468    /**
469     * Sets the wall time in milliseconds when this print job was created.
470     *
471     * @param creationTime The creation time in milliseconds.
472     *
473     * @hide
474     */
475    public void setCreationTime(long creationTime) {
476        if (creationTime < 0) {
477            throw new IllegalArgumentException("creationTime must be non-negative.");
478        }
479        mCreationTime = creationTime;
480    }
481
482    /**
483     * Gets the number of copies.
484     *
485     * @return The number of copies or zero if not set.
486     */
487    public @IntRange(from = 0) int getCopies() {
488        return mCopies;
489    }
490
491    /**
492     * Sets the number of copies.
493     *
494     * @param copyCount The number of copies.
495     *
496     * @hide
497     */
498    public void setCopies(int copyCount) {
499        if (copyCount < 1) {
500            throw new IllegalArgumentException("Copies must be more than one.");
501        }
502        mCopies = copyCount;
503    }
504
505    /**
506     * Gets the included pages.
507     *
508     * @return The included pages or <code>null</code> if not set.
509     */
510    public @Nullable PageRange[] getPages() {
511        return mPageRanges;
512    }
513
514    /**
515     * Sets the included pages.
516     *
517     * @param pageRanges The included pages.
518     *
519     * @hide
520     */
521    public void setPages(PageRange[] pageRanges) {
522        mPageRanges = pageRanges;
523    }
524
525    /**
526     * Gets the print job attributes.
527     *
528     * @return The attributes.
529     */
530    public @NonNull PrintAttributes getAttributes() {
531        return mAttributes;
532    }
533
534    /**
535     * Sets the print job attributes.
536     *
537     * @param attributes The attributes.
538     *
539     * @hide
540     */
541    public void setAttributes(PrintAttributes attributes) {
542        mAttributes = attributes;
543    }
544
545    /**
546     * Gets the info describing the printed document.
547     *
548     * @return The document info.
549     *
550     * @hide
551     */
552    public PrintDocumentInfo getDocumentInfo() {
553        return mDocumentInfo;
554    }
555
556    /**
557     * Sets the info describing the printed document.
558     *
559     * @param info The document info.
560     *
561     * @hide
562     */
563    public void setDocumentInfo(PrintDocumentInfo info) {
564        mDocumentInfo = info;
565    }
566
567    /**
568     * Gets whether this print is being cancelled.
569     *
570     * @return True if the print job is being cancelled.
571     *
572     * @hide
573     */
574    public boolean isCancelling() {
575        return mCanceling;
576    }
577
578    /**
579     * Sets whether this print is being cancelled.
580     *
581     * @param cancelling True if the print job is being cancelled.
582     *
583     * @hide
584     */
585    public void setCancelling(boolean cancelling) {
586        mCanceling = cancelling;
587    }
588
589    /**
590     * Gets whether this job has a given advanced (printer specific) print
591     * option.
592     *
593     * @param key The option key.
594     * @return Whether the option is present.
595     */
596    public boolean hasAdvancedOption(String key) {
597        return mAdvancedOptions != null && mAdvancedOptions.containsKey(key);
598    }
599
600    /**
601     * Gets the value of an advanced (printer specific) print option.
602     *
603     * @param key The option key.
604     * @return The option value.
605     */
606    public String getAdvancedStringOption(String key) {
607        if (mAdvancedOptions != null) {
608            return mAdvancedOptions.getString(key);
609        }
610        return null;
611    }
612
613    /**
614     * Gets the value of an advanced (printer specific) print option.
615     *
616     * @param key The option key.
617     * @return The option value.
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