PrintJob.java revision d26d4898fcc9b78f4b66118895c375384098205e
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.printservice;
18
19import android.os.RemoteException;
20import android.print.PrintJobInfo;
21import android.text.TextUtils;
22import android.util.Log;
23
24/**
25 * This class represents a print job from the perspective of a print
26 * service. It provides APIs for observing the print job state and
27 * performing operations on the print job.
28 * <p>
29 * <strong>Note: </strong> All methods of this class must be executed on the main
30 * application thread.
31 * </p>
32 */
33public final class PrintJob {
34
35    private static final String LOG_TAG = "PrintJob";
36
37    private final IPrintServiceClient mPrintServiceClient;
38
39    private final PrintDocument mDocument;
40
41    private PrintJobInfo mCachedInfo;
42
43    PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
44        mCachedInfo = jobInfo;
45        mPrintServiceClient = client;
46        mDocument = new PrintDocument(mCachedInfo.getId(), client,
47                jobInfo.getDocumentInfo());
48    }
49
50    /**
51     * Gets the unique print job id.
52     *
53     * @return The id.
54     */
55    public int getId() {
56        PrintService.throwIfNotCalledOnMainThread();
57        return mCachedInfo.getId();
58    }
59
60    /**
61     * Gets the {@link PrintJobInfo} that describes this job.
62     * <p>
63     * <strong>Node:</strong>The returned info object is a snapshot of the
64     * current print job state. Every call to this method returns a fresh
65     * info object that reflects the current print job state.
66     * </p>
67     *
68     * @return The print job info.
69     */
70    public PrintJobInfo getInfo() {
71        PrintService.throwIfNotCalledOnMainThread();
72        if (isInImmutableState()) {
73            return mCachedInfo;
74        }
75        PrintJobInfo info = null;
76        try {
77            info = mPrintServiceClient.getPrintJobInfo(mCachedInfo.getId());
78        } catch (RemoteException re) {
79            Log.e(LOG_TAG, "Couldn't get info for job: " + mCachedInfo.getId(), re);
80        }
81        if (info != null) {
82            mCachedInfo = info;
83        }
84        return mCachedInfo;
85    }
86
87    /**
88     * Gets the printed document.
89     *
90     * @return The document.
91     */
92    public PrintDocument getDocument() {
93        PrintService.throwIfNotCalledOnMainThread();
94        return mDocument;
95    }
96
97    /**
98     * Gets whether this print job is queued. Such a print job is
99     * ready to be printed and can be started or cancelled.
100     *
101     * @return Whether the print job is queued.
102     *
103     * @see #start()
104     * @see #cancel()
105     */
106    public boolean isQueued() {
107        PrintService.throwIfNotCalledOnMainThread();
108        return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
109    }
110
111    /**
112     * Gets whether this print job is started. Such a print job is
113     * being printed and can be completed or canceled or failed.
114     *
115     * @return Whether the print job is started.
116     *
117     * @see #complete()
118     * @see #cancel()
119     * @see #fail(CharSequence)
120     */
121    public boolean isStarted() {
122        PrintService.throwIfNotCalledOnMainThread();
123        return getInfo().getState() == PrintJobInfo.STATE_STARTED;
124    }
125
126    /**
127     * Gets whether this print job is blocked. Such a print job is halted
128     * due to an abnormal condition and can be started or canceled or failed.
129     *
130     * @return Whether the print job is blocked.
131     *
132     * @see #start()
133     * @see #cancel()
134     * @see #fail(CharSequence)
135     */
136    public boolean isBlocked() {
137        PrintService.throwIfNotCalledOnMainThread();
138        return getInfo().getState() == PrintJobInfo.STATE_BLOCKED;
139    }
140
141    /**
142     * Gets whether this print job is completed. Such a print job
143     * is successfully printed. This is a final state.
144     *
145     * @return Whether the print job is completed.
146     *
147     * @see #complete()
148     */
149    public boolean isCompleted() {
150        PrintService.throwIfNotCalledOnMainThread();
151        return getInfo().getState() == PrintJobInfo.STATE_COMPLETED;
152    }
153
154    /**
155     * Gets whether this print job is failed. Such a print job is
156     * not successfully printed due to an error. This is a final state.
157     *
158     * @return Whether the print job is failed.
159     *
160     * @see #fail(CharSequence)
161     */
162    public boolean isFailed() {
163        PrintService.throwIfNotCalledOnMainThread();
164        return getInfo().getState() == PrintJobInfo.STATE_FAILED;
165    }
166
167    /**
168     * Gets whether this print job is cancelled. Such a print job was
169     * cancelled as a result of a user request. This is a final state.
170     *
171     * @return Whether the print job is cancelled.
172     *
173     * @see #cancel()
174     */
175    public boolean isCancelled() {
176        PrintService.throwIfNotCalledOnMainThread();
177        return getInfo().getState() == PrintJobInfo.STATE_FAILED;
178    }
179
180    /**
181     * Starts the print job. You should call this method if {@link
182     * #isQueued()} or {@link #isBlocked()} returns true and you started
183     * resumed printing.
184     *
185     * @return Whether the job was started.
186     *
187     * @see #isQueued()
188     * @see #isBlocked()
189     */
190    public boolean start() {
191        PrintService.throwIfNotCalledOnMainThread();
192        final int state = getInfo().getState();
193        if (state == PrintJobInfo.STATE_QUEUED
194                || state == PrintJobInfo.STATE_BLOCKED) {
195            return setState(PrintJobInfo.STATE_STARTED, null);
196        }
197        return false;
198    }
199
200    /**
201     * Blocks the print job. You should call this method if {@link
202     * #isStarted()} or {@link #isBlocked()} returns true and you need
203     * to block the print job. For example, the user has to add some
204     * paper to continue printing. To resume the print job call {@link
205     * #start()}.
206     *
207     * @return Whether the job was blocked.
208     *
209     * @see #isStarted()
210     * @see #isBlocked()
211     */
212    public boolean block(String reason) {
213        PrintService.throwIfNotCalledOnMainThread();
214        PrintJobInfo info = getInfo();
215        final int state = info.getState();
216        if (state == PrintJobInfo.STATE_STARTED
217                || (state == PrintJobInfo.STATE_BLOCKED
218                        && !TextUtils.equals(info.getStateReason(), reason))) {
219            return setState(PrintJobInfo.STATE_BLOCKED, reason);
220        }
221        return false;
222    }
223
224    /**
225     * Completes the print job. You should call this method if {@link
226     * #isStarted()} returns true and you are done printing.
227     *
228     * @return Whether the job as completed.
229     *
230     * @see #isStarted()
231     */
232    public boolean complete() {
233        PrintService.throwIfNotCalledOnMainThread();
234        if (isStarted()) {
235            return setState(PrintJobInfo.STATE_COMPLETED, null);
236        }
237        return false;
238    }
239
240    /**
241     * Fails the print job. You should call this method if {@link
242     * #isQueued()} or {@link #isStarted()} or {@link #isBlocked()}
243     * returns true you failed while printing.
244     *
245     * @param error The human readable, short, and translated reason
246     * for the failure.
247     * @return Whether the job was failed.
248     *
249     * @see #isQueued()
250     * @see #isStarted()
251     * @see #isBlocked()
252     */
253    public boolean fail(String error) {
254        PrintService.throwIfNotCalledOnMainThread();
255        if (!isInImmutableState()) {
256            return setState(PrintJobInfo.STATE_FAILED, error);
257        }
258        return false;
259    }
260
261    /**
262     * Cancels the print job. You should call this method if {@link
263     * #isQueued()} or {@link #isStarted() or #isBlocked()} returns
264     * true and you canceled the print job as a response to a call to
265     * {@link PrintService#onRequestCancelPrintJob(PrintJob)}.
266     *
267     * @return Whether the job is canceled.
268     *
269     * @see #isStarted()
270     * @see #isQueued()
271     * @see #isBlocked()
272     */
273    public boolean cancel() {
274        PrintService.throwIfNotCalledOnMainThread();
275        if (!isInImmutableState()) {
276            return setState(PrintJobInfo.STATE_CANCELED, null);
277        }
278        return false;
279    }
280
281    /**
282     * Sets a tag that is valid in the context of a {@link PrintService}
283     * and is not interpreted by the system. For example, a print service
284     * may set as a tag the key of the print job returned by a remote
285     * print server, if the printing is off handed to a cloud based service.
286     *
287     * @param tag The tag.
288     * @return True if the tag was set, false otherwise.
289     */
290    public boolean setTag(String tag) {
291        PrintService.throwIfNotCalledOnMainThread();
292        if (isInImmutableState()) {
293            return false;
294        }
295        try {
296            return mPrintServiceClient.setPrintJobTag(mCachedInfo.getId(), tag);
297        } catch (RemoteException re) {
298            Log.e(LOG_TAG, "Error setting tag for job: " + mCachedInfo.getId(), re);
299        }
300        return false;
301    }
302
303    @Override
304    public boolean equals(Object obj) {
305        if (this == obj) {
306            return true;
307        }
308        if (obj == null) {
309            return false;
310        }
311        if (getClass() != obj.getClass()) {
312            return false;
313        }
314        PrintJob other = (PrintJob) obj;
315        return (mCachedInfo.getId() == other.mCachedInfo.getId());
316    }
317
318    @Override
319    public int hashCode() {
320        return mCachedInfo.getId();
321    }
322
323    private boolean isInImmutableState() {
324        final int state = mCachedInfo.getState();
325        return state == PrintJobInfo.STATE_COMPLETED
326                || state == PrintJobInfo.STATE_CANCELED
327                || state == PrintJobInfo.STATE_FAILED;
328    }
329
330    private boolean setState(int state, String error) {
331        try {
332            if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) {
333                // Best effort - update the state of the cached info since
334                // we may not be able to re-fetch it later if the job gets
335                // removed from the spooler as a result of the state change.
336                mCachedInfo.setState(state);
337                mCachedInfo.setStateReason(error);
338                return true;
339            }
340        } catch (RemoteException re) {
341            Log.e(LOG_TAG, "Error setting the state of job: " + mCachedInfo.getId(), re);
342        }
343        return false;
344    }
345}
346