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