blob: 3fb812e401f7345b72f3603da7ef20a809c4cfd8 [file] [log] [blame]
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001/*
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
Svetoslav Ganov858a1852013-10-17 22:20:40 -070019import android.app.Activity;
20import android.app.Application.ActivityLifecycleCallbacks;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070021import android.content.Context;
22import android.content.IntentSender;
23import android.content.IntentSender.SendIntentException;
Svetoslav62836082013-07-17 14:52:35 -070024import android.os.Bundle;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070025import android.os.CancellationSignal;
26import android.os.Handler;
Svetoslava798c0a2014-05-15 10:47:19 -070027import android.os.ICancellationSignal;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070028import android.os.Looper;
29import android.os.Message;
30import android.os.ParcelFileDescriptor;
31import android.os.RemoteException;
Svetoslav Ganova0027152013-06-25 14:59:53 -070032import android.print.PrintDocumentAdapter.LayoutResultCallback;
33import android.print.PrintDocumentAdapter.WriteResultCallback;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070034import android.printservice.PrintServiceInfo;
Svetoslav Ganov88d19912013-07-22 12:32:03 -070035import android.text.TextUtils;
Svetoslav Ganov704697b2013-09-21 20:30:24 -070036import android.util.ArrayMap;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070037import android.util.Log;
38
39import com.android.internal.os.SomeArgs;
40
41import libcore.io.IoUtils;
42
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070043import java.lang.ref.WeakReference;
44import java.util.ArrayList;
Svetoslava798c0a2014-05-15 10:47:19 -070045import java.util.Arrays;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070046import java.util.Collections;
47import java.util.List;
Svetoslav Ganov704697b2013-09-21 20:30:24 -070048import java.util.Map;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070049
50/**
51 * System level service for accessing the printing capabilities of the platform.
52 * <p>
53 * To obtain a handle to the print manager do the following:
54 * </p>
Svetoslava798c0a2014-05-15 10:47:19 -070055 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070056 * <pre>
57 * PrintManager printManager =
58 * (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
59 * </pre>
Svetoslava798c0a2014-05-15 10:47:19 -070060 *
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -070061 * <h3>Print mechanics</h3>
62 * <p>
63 * The key idea behind printing on the platform is that the content to be printed
64 * should be laid out for the currently selected print options resulting in an
65 * optimized output and higher user satisfaction. To achieve this goal the platform
66 * declares a contract that the printing application has to follow which is defined
67 * by the {@link PrintDocumentAdapter} class. At a higher level the contract is that
68 * when the user selects some options from the print UI that may affect the way
69 * content is laid out, for example page size, the application receives a callback
70 * allowing it to layout the content to better fit these new constraints. After a
71 * layout pass the system may ask the application to render one or more pages one
72 * or more times. For example, an application may produce a single column list for
73 * smaller page sizes and a multi-column table for larger page sizes.
74 * </p>
75 * <h3>Print jobs</h3>
76 * <p>
77 * Print jobs are started by calling the {@link #print(String, PrintDocumentAdapter,
78 * PrintAttributes)} from an activity which results in bringing up the system print
79 * UI. Once the print UI is up, when the user changes a selected print option that
80 * affects the way content is laid out the system starts to interact with the
81 * application following the mechanics described the section above.
82 * </p>
83 * <p>
84 * Print jobs can be in {@link PrintJobInfo#STATE_CREATED created}, {@link
85 * PrintJobInfo#STATE_QUEUED queued}, {@link PrintJobInfo#STATE_STARTED started},
86 * {@link PrintJobInfo#STATE_BLOCKED blocked}, {@link PrintJobInfo#STATE_COMPLETED
87 * completed}, {@link PrintJobInfo#STATE_FAILED failed}, and {@link
88 * PrintJobInfo#STATE_CANCELED canceled} state. Print jobs are stored in dedicated
89 * system spooler until they are handled which is they are cancelled or completed.
90 * Active print jobs, ones that are not cancelled or completed, are considered failed
91 * if the device reboots as the new boot may be after a very long time. The user may
92 * choose to restart such print jobs. Once a print job is queued all relevant content
93 * is stored in the system spooler and its lifecycle becomes detached from this of
94 * the application that created it.
95 * </p>
96 * <p>
97 * An applications can query the print spooler for current print jobs it created
98 * but not print jobs created by other applications.
99 * </p>
100 *
101 * @see PrintJob
102 * @see PrintJobInfo
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700103 */
104public final class PrintManager {
105
106 private static final String LOG_TAG = "PrintManager";
107
Svetoslav15cbc8a2014-07-11 09:45:07 -0700108 private static final boolean DEBUG = false;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700109
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700110 private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
111
112 /**
113 * The action for launching the print dialog activity.
114 *
115 * @hide
116 */
117 public static final String ACTION_PRINT_DIALOG = "android.print.PRINT_DIALOG";
118
119 /**
120 * Extra with the intent for starting the print dialog.
121 * <p>
122 * <strong>Type:</strong> {@link android.content.IntentSender}
123 * </p>
124 *
125 * @hide
126 */
127 public static final String EXTRA_PRINT_DIALOG_INTENT =
128 "android.print.intent.extra.EXTRA_PRINT_DIALOG_INTENT";
129
130 /**
131 * Extra with a print job.
132 * <p>
133 * <strong>Type:</strong> {@link android.print.PrintJobInfo}
134 * </p>
135 *
136 * @hide
137 */
138 public static final String EXTRA_PRINT_JOB =
139 "android.print.intent.extra.EXTRA_PRINT_JOB";
140
141 /**
142 * Extra with the print document adapter to be printed.
143 * <p>
144 * <strong>Type:</strong> {@link android.print.IPrintDocumentAdapter}
145 * </p>
146 *
147 * @hide
148 */
149 public static final String EXTRA_PRINT_DOCUMENT_ADAPTER =
150 "android.print.intent.extra.EXTRA_PRINT_DOCUMENT_ADAPTER";
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700151
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700152 /** @hide */
153 public static final int APP_ID_ANY = -2;
154
155 private final Context mContext;
156
157 private final IPrintManager mService;
158
159 private final int mUserId;
160
161 private final int mAppId;
162
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700163 private final Handler mHandler;
164
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700165 private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper> mPrintJobStateChangeListeners;
166
167 /** @hide */
168 public interface PrintJobStateChangeListener {
169
170 /**
171 * Callback notifying that a print job state changed.
Svetoslav2235a172014-05-08 14:19:01 -0700172 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700173 * @param printJobId The print job id.
174 */
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700175 public void onPrintJobStateChanged(PrintJobId printJobId);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700176 }
177
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700178 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700179 * Creates a new instance.
Svetoslav2235a172014-05-08 14:19:01 -0700180 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700181 * @param context The current context in which to operate.
182 * @param service The backing system service.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700183 * @hide
184 */
185 public PrintManager(Context context, IPrintManager service, int userId, int appId) {
186 mContext = context;
187 mService = service;
188 mUserId = userId;
189 mAppId = appId;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700190 mHandler = new Handler(context.getMainLooper(), null, false) {
191 @Override
192 public void handleMessage(Message message) {
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700193 switch (message.what) {
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700194 case MSG_NOTIFY_PRINT_JOB_STATE_CHANGED: {
195 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -0700196 PrintJobStateChangeListenerWrapper wrapper =
197 (PrintJobStateChangeListenerWrapper) args.arg1;
198 PrintJobStateChangeListener listener = wrapper.getListener();
199 if (listener != null) {
200 PrintJobId printJobId = (PrintJobId) args.arg2;
201 listener.onPrintJobStateChanged(printJobId);
202 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700203 args.recycle();
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700204 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700205 }
206 }
207 };
208 }
209
210 /**
211 * Creates an instance that can access all print jobs.
Svetoslav2235a172014-05-08 14:19:01 -0700212 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700213 * @param userId The user id for which to get all print jobs.
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700214 * @return An instance if the caller has the permission to access all print
215 * jobs, null otherwise.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700216 * @hide
217 */
218 public PrintManager getGlobalPrintManagerForUser(int userId) {
Svetoslav2235a172014-05-08 14:19:01 -0700219 if (mService == null) {
220 Log.w(LOG_TAG, "Feature android.software.print not available");
221 return null;
222 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700223 return new PrintManager(mContext, mService, userId, APP_ID_ANY);
224 }
225
Svetoslav2fbd2a72013-09-16 17:53:51 -0700226 PrintJobInfo getPrintJobInfo(PrintJobId printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700227 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700228 return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700229 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700230 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700231 }
232 return null;
233 }
234
235 /**
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700236 * Adds a listener for observing the state of print jobs.
Svetoslav2235a172014-05-08 14:19:01 -0700237 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700238 * @param listener The listener to add.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700239 * @hide
240 */
241 public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) {
Svetoslav2235a172014-05-08 14:19:01 -0700242 if (mService == null) {
243 Log.w(LOG_TAG, "Feature android.software.print not available");
244 return;
245 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700246 if (mPrintJobStateChangeListeners == null) {
247 mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener,
248 PrintJobStateChangeListenerWrapper>();
249 }
250 PrintJobStateChangeListenerWrapper wrappedListener =
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700251 new PrintJobStateChangeListenerWrapper(listener, mHandler);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700252 try {
253 mService.addPrintJobStateChangeListener(wrappedListener, mAppId, mUserId);
254 mPrintJobStateChangeListeners.put(listener, wrappedListener);
255 } catch (RemoteException re) {
256 Log.e(LOG_TAG, "Error adding print job state change listener", re);
257 }
258 }
259
260 /**
261 * Removes a listener for observing the state of print jobs.
Svetoslav2235a172014-05-08 14:19:01 -0700262 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700263 * @param listener The listener to remove.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700264 * @hide
265 */
266 public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) {
Svetoslav2235a172014-05-08 14:19:01 -0700267 if (mService == null) {
268 Log.w(LOG_TAG, "Feature android.software.print not available");
269 return;
270 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700271 if (mPrintJobStateChangeListeners == null) {
272 return;
273 }
274 PrintJobStateChangeListenerWrapper wrappedListener =
275 mPrintJobStateChangeListeners.remove(listener);
276 if (wrappedListener == null) {
277 return;
278 }
279 if (mPrintJobStateChangeListeners.isEmpty()) {
280 mPrintJobStateChangeListeners = null;
281 }
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -0700282 wrappedListener.destroy();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700283 try {
284 mService.removePrintJobStateChangeListener(wrappedListener, mUserId);
285 } catch (RemoteException re) {
286 Log.e(LOG_TAG, "Error removing print job state change listener", re);
287 }
288 }
289
290 /**
291 * Gets a print job given its id.
Svetoslav2235a172014-05-08 14:19:01 -0700292 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700293 * @return The print job list.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700294 * @see PrintJob
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700295 * @hide
296 */
297 public PrintJob getPrintJob(PrintJobId printJobId) {
Svetoslav2235a172014-05-08 14:19:01 -0700298 if (mService == null) {
299 Log.w(LOG_TAG, "Feature android.software.print not available");
300 return null;
301 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700302 try {
303 PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId);
304 if (printJob != null) {
305 return new PrintJob(printJob, this);
306 }
307 } catch (RemoteException re) {
308 Log.e(LOG_TAG, "Error getting print job", re);
309 }
310 return null;
311 }
312
313 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700314 * Gets the print jobs for this application.
Svetoslav2235a172014-05-08 14:19:01 -0700315 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700316 * @return The print job list.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700317 * @see PrintJob
318 */
319 public List<PrintJob> getPrintJobs() {
Svetoslav2235a172014-05-08 14:19:01 -0700320 if (mService == null) {
321 Log.w(LOG_TAG, "Feature android.software.print not available");
322 return Collections.emptyList();
323 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700324 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700325 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700326 if (printJobInfos == null) {
327 return Collections.emptyList();
328 }
329 final int printJobCount = printJobInfos.size();
330 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
331 for (int i = 0; i < printJobCount; i++) {
332 printJobs.add(new PrintJob(printJobInfos.get(i), this));
333 }
334 return printJobs;
335 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700336 Log.e(LOG_TAG, "Error getting print jobs", re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700337 }
338 return Collections.emptyList();
339 }
340
Svetoslav2fbd2a72013-09-16 17:53:51 -0700341 void cancelPrintJob(PrintJobId printJobId) {
Svetoslav2235a172014-05-08 14:19:01 -0700342 if (mService == null) {
343 Log.w(LOG_TAG, "Feature android.software.print not available");
344 return;
345 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700346 try {
347 mService.cancelPrintJob(printJobId, mAppId, mUserId);
348 } catch (RemoteException re) {
Svetoslava798c0a2014-05-15 10:47:19 -0700349 Log.e(LOG_TAG, "Error canceling a print job: " + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700350 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700351 }
352
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700353 void restartPrintJob(PrintJobId printJobId) {
Svetoslav2235a172014-05-08 14:19:01 -0700354 if (mService == null) {
355 Log.w(LOG_TAG, "Feature android.software.print not available");
356 return;
357 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700358 try {
359 mService.restartPrintJob(printJobId, mAppId, mUserId);
360 } catch (RemoteException re) {
361 Log.e(LOG_TAG, "Error restarting a print job: " + printJobId, re);
362 }
363 }
364
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700365 /**
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700366 * Creates a print job for printing a {@link PrintDocumentAdapter} with
367 * default print attributes.
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700368 * <p>
369 * Calling this method brings the print UI allowing the user to customize
370 * the print job and returns a {@link PrintJob} object without waiting for the
371 * user to customize or confirm the print job. The returned print job instance
372 * is in a {@link PrintJobInfo#STATE_CREATED created} state.
373 * <p>
374 * This method can be called only from an {@link Activity}. The rationale is that
375 * printing from a service will create an inconsistent user experience as the print
376 * UI would appear without any context.
377 * </p>
378 * <p>
379 * Also the passed in {@link PrintDocumentAdapter} will be considered invalid if
380 * your activity is finished. The rationale is that once the activity that
381 * initiated printing is finished, the provided adapter may be in an inconsistent
382 * state as it may depend on the UI presented by the activity.
383 * </p>
384 * <p>
385 * The default print attributes are a hint to the system how the data is to
386 * be printed. For example, a photo editor may look at the photo aspect ratio
387 * to determine the default orientation and provide a hint whether the printing
388 * should be in portrait or landscape. The system will do a best effort to
389 * selected the hinted options in the print dialog, given the current printer
390 * supports them.
391 * </p>
Svetoslav81f14b92014-03-18 10:37:20 -0700392 * <p>
393 * <strong>Note:</strong> Calling this method will bring the print dialog and
394 * the system will connect to the provided {@link PrintDocumentAdapter}. If a
Svetoslav85a85a02014-03-18 10:48:51 -0700395 * configuration change occurs that you application does not handle, for example
396 * a rotation change, the system will drop the connection to the adapter as the
397 * activity has to be recreated and the old adapter may be invalid in this context,
398 * hence a new adapter instance is required. As a consequence, if your activity
399 * does not handle configuration changes (default behavior), you have to save the
400 * state that you were printing and call this method again when your activity
401 * is recreated.
Svetoslav81f14b92014-03-18 10:37:20 -0700402 * </p>
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700403 *
404 * @param printJobName A name for the new print job which is shown to the user.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700405 * @param documentAdapter An adapter that emits the document to print.
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700406 * @param attributes The default print job attributes or <code>null</code>.
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700407 * @return The created print job on success or null on failure.
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700408 * @throws IllegalStateException If not called from an {@link Activity}.
409 * @throws IllegalArgumentException If the print job name is empty or the
410 * document adapter is null.
411 *
Svetoslavfd906512013-06-24 09:04:48 -0700412 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700413 */
Svetoslav Ganova0027152013-06-25 14:59:53 -0700414 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700415 PrintAttributes attributes) {
Svetoslav2235a172014-05-08 14:19:01 -0700416 if (mService == null) {
417 Log.w(LOG_TAG, "Feature android.software.print not available");
418 return null;
419 }
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700420 if (!(mContext instanceof Activity)) {
421 throw new IllegalStateException("Can print only from an activity");
422 }
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700423 if (TextUtils.isEmpty(printJobName)) {
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700424 throw new IllegalArgumentException("printJobName cannot be empty");
425 }
426 if (documentAdapter == null) {
427 throw new IllegalArgumentException("documentAdapter cannot be null");
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700428 }
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700429 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700430 (Activity) mContext, documentAdapter);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700431 try {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700432 Bundle result = mService.print(printJobName, delegate,
433 attributes, mContext.getPackageName(), mAppId, mUserId);
434 if (result != null) {
435 PrintJobInfo printJob = result.getParcelable(EXTRA_PRINT_JOB);
436 IntentSender intent = result.getParcelable(EXTRA_PRINT_DIALOG_INTENT);
437 if (printJob == null || intent == null) {
438 return null;
439 }
440 try {
441 mContext.startIntentSender(intent, null, 0, 0, 0);
442 return new PrintJob(printJob, this);
443 } catch (SendIntentException sie) {
444 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
445 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700446 }
447 } catch (RemoteException re) {
448 Log.e(LOG_TAG, "Error creating a print job", re);
449 }
450 return null;
451 }
452
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700453 /**
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700454 * Gets the list of enabled print services.
Svetoslav2235a172014-05-08 14:19:01 -0700455 *
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700456 * @return The enabled service list or an empty list.
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700457 * @hide
458 */
459 public List<PrintServiceInfo> getEnabledPrintServices() {
Svetoslav2235a172014-05-08 14:19:01 -0700460 if (mService == null) {
461 Log.w(LOG_TAG, "Feature android.software.print not available");
462 return Collections.emptyList();
463 }
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700464 try {
465 List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId);
466 if (enabledServices != null) {
467 return enabledServices;
468 }
469 } catch (RemoteException re) {
Svetoslavd8dbc132013-09-27 18:29:53 -0700470 Log.e(LOG_TAG, "Error getting the enabled print services", re);
471 }
472 return Collections.emptyList();
473 }
474
475 /**
476 * Gets the list of installed print services.
Svetoslav2235a172014-05-08 14:19:01 -0700477 *
Svetoslavd8dbc132013-09-27 18:29:53 -0700478 * @return The installed service list or an empty list.
Svetoslavd8dbc132013-09-27 18:29:53 -0700479 * @hide
480 */
481 public List<PrintServiceInfo> getInstalledPrintServices() {
Svetoslav2235a172014-05-08 14:19:01 -0700482 if (mService == null) {
483 Log.w(LOG_TAG, "Feature android.software.print not available");
484 return Collections.emptyList();
485 }
Svetoslavd8dbc132013-09-27 18:29:53 -0700486 try {
487 List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId);
488 if (installedServices != null) {
489 return installedServices;
490 }
491 } catch (RemoteException re) {
492 Log.e(LOG_TAG, "Error getting the installed print services", re);
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700493 }
494 return Collections.emptyList();
495 }
496
497 /**
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700498 * @hide
499 */
500 public PrinterDiscoverySession createPrinterDiscoverySession() {
Svetoslav2235a172014-05-08 14:19:01 -0700501 if (mService == null) {
502 Log.w(LOG_TAG, "Feature android.software.print not available");
503 return null;
504 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700505 return new PrinterDiscoverySession(mService, mContext, mUserId);
506 }
507
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700508 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
509 implements ActivityLifecycleCallbacks {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700510 private final Object mLock = new Object();
511
Svetoslava798c0a2014-05-15 10:47:19 -0700512 private Activity mActivity; // Strong reference OK - cleared in destroy
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700513
Svetoslava798c0a2014-05-15 10:47:19 -0700514 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in destroy
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700515
Svetoslava798c0a2014-05-15 10:47:19 -0700516 private Handler mHandler; // Strong reference OK - cleared in destroy
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700517
Svetoslava798c0a2014-05-15 10:47:19 -0700518 private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in destroy
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700519
Svetoslava798c0a2014-05-15 10:47:19 -0700520 private DestroyableCallback mPendingCallback;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700521
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700522 public PrintDocumentAdapterDelegate(Activity activity,
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700523 PrintDocumentAdapter documentAdapter) {
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700524 mActivity = activity;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700525 mDocumentAdapter = documentAdapter;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700526 mHandler = new MyHandler(mActivity.getMainLooper());
527 mActivity.getApplication().registerActivityLifecycleCallbacks(this);
528 }
529
530 @Override
531 public void setObserver(IPrintDocumentAdapterObserver observer) {
532 final boolean destroyed;
533 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700534 mObserver = observer;
535 destroyed = isDestroyedLocked();
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700536 }
Svetoslava798c0a2014-05-15 10:47:19 -0700537
Svetoslav6552bf32014-09-03 21:15:55 -0700538 if (destroyed && observer != null) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700539 try {
540 observer.onDestroy();
541 } catch (RemoteException re) {
542 Log.e(LOG_TAG, "Error announcing destroyed state", re);
543 }
544 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700545 }
546
547 @Override
548 public void start() {
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700549 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700550 // If destroyed the handler is null.
551 if (!isDestroyedLocked()) {
552 mHandler.obtainMessage(MyHandler.MSG_ON_START,
553 mDocumentAdapter).sendToTarget();
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700554 }
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700555 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700556 }
557
558 @Override
Svetoslav62836082013-07-17 14:52:35 -0700559 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700560 ILayoutResultCallback callback, Bundle metadata, int sequence) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700561
Svetoslava798c0a2014-05-15 10:47:19 -0700562 ICancellationSignal cancellationTransport = CancellationSignal.createTransport();
563 try {
564 callback.onLayoutStarted(cancellationTransport, sequence);
565 } catch (RemoteException re) {
566 // The spooler is dead - can't recover.
567 Log.e(LOG_TAG, "Error notifying for layout start", re);
568 return;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700569 }
Svetoslava798c0a2014-05-15 10:47:19 -0700570
571 synchronized (mLock) {
572 // If destroyed the handler is null.
573 if (isDestroyedLocked()) {
574 return;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700575 }
Svetoslava798c0a2014-05-15 10:47:19 -0700576
577 CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
578 cancellationTransport);
579
580 SomeArgs args = SomeArgs.obtain();
581 args.arg1 = mDocumentAdapter;
582 args.arg2 = oldAttributes;
583 args.arg3 = newAttributes;
584 args.arg4 = cancellationSignal;
585 args.arg5 = new MyLayoutResultCallback(callback, sequence);
586 args.arg6 = metadata;
587
588 mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT, args).sendToTarget();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700589 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700590 }
591
592 @Override
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700593 public void write(PageRange[] pages, ParcelFileDescriptor fd,
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700594 IWriteResultCallback callback, int sequence) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700595
Svetoslava798c0a2014-05-15 10:47:19 -0700596 ICancellationSignal cancellationTransport = CancellationSignal.createTransport();
597 try {
598 callback.onWriteStarted(cancellationTransport, sequence);
599 } catch (RemoteException re) {
600 // The spooler is dead - can't recover.
601 Log.e(LOG_TAG, "Error notifying for write start", re);
602 return;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700603 }
Svetoslava798c0a2014-05-15 10:47:19 -0700604
605 synchronized (mLock) {
606 // If destroyed the handler is null.
607 if (isDestroyedLocked()) {
608 return;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700609 }
Svetoslava798c0a2014-05-15 10:47:19 -0700610
611 CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
612 cancellationTransport);
613
614 SomeArgs args = SomeArgs.obtain();
615 args.arg1 = mDocumentAdapter;
616 args.arg2 = pages;
617 args.arg3 = fd;
618 args.arg4 = cancellationSignal;
619 args.arg5 = new MyWriteResultCallback(callback, fd, sequence);
620
621 mHandler.obtainMessage(MyHandler.MSG_ON_WRITE, args).sendToTarget();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700622 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700623 }
624
625 @Override
626 public void finish() {
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700627 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700628 // If destroyed the handler is null.
629 if (!isDestroyedLocked()) {
630 mHandler.obtainMessage(MyHandler.MSG_ON_FINISH,
631 mDocumentAdapter).sendToTarget();
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700632 }
Svetoslavd270cb92013-10-31 14:27:16 -0700633 }
634 }
635
636 @Override
Svet Ganovfce84f02014-10-31 16:56:52 -0700637 public void kill(String reason) {
638 synchronized (mLock) {
639 // If destroyed the handler is null.
640 if (!isDestroyedLocked()) {
641 mHandler.obtainMessage(MyHandler.MSG_ON_KILL,
642 reason).sendToTarget();
643 }
644 }
645 }
646
647 @Override
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700648 public void onActivityPaused(Activity activity) {
649 /* do nothing */
650 }
651
652 @Override
653 public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
654 /* do nothing */
655 }
656
657 @Override
658 public void onActivityStarted(Activity activity) {
659 /* do nothing */
660 }
661
662 @Override
663 public void onActivityResumed(Activity activity) {
664 /* do nothing */
665 }
666
667 @Override
668 public void onActivityStopped(Activity activity) {
669 /* do nothing */
670 }
671
672 @Override
673 public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
674 /* do nothing */
675 }
676
677 @Override
678 public void onActivityDestroyed(Activity activity) {
679 // We really care only if the activity is being destroyed to
680 // notify the the print spooler so it can close the print dialog.
681 // Note the the spooler has a death recipient that observes if
682 // this process gets killed so we cover the case of onDestroy not
683 // being called due to this process being killed to reclaim memory.
Svetoslava798c0a2014-05-15 10:47:19 -0700684 IPrintDocumentAdapterObserver observer = null;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700685 synchronized (mLock) {
686 if (activity == mActivity) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700687 observer = mObserver;
Svetoslava798c0a2014-05-15 10:47:19 -0700688 destroyLocked();
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700689 }
690 }
691 if (observer != null) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700692 try {
693 observer.onDestroy();
694 } catch (RemoteException re) {
695 Log.e(LOG_TAG, "Error announcing destroyed state", re);
696 }
697 }
698 }
699
Svetoslava798c0a2014-05-15 10:47:19 -0700700 private boolean isDestroyedLocked() {
701 return (mActivity == null);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700702 }
703
Svetoslava798c0a2014-05-15 10:47:19 -0700704 private void destroyLocked() {
705 mActivity.getApplication().unregisterActivityLifecycleCallbacks(
706 PrintDocumentAdapterDelegate.this);
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700707 mActivity = null;
Svetoslava798c0a2014-05-15 10:47:19 -0700708
Svetoslav Ganova0027152013-06-25 14:59:53 -0700709 mDocumentAdapter = null;
Svetoslava798c0a2014-05-15 10:47:19 -0700710
711 // This method is only called from the main thread, so
712 // clearing the messages guarantees that any time a
713 // message is handled we are not in a destroyed state.
714 mHandler.removeMessages(MyHandler.MSG_ON_START);
715 mHandler.removeMessages(MyHandler.MSG_ON_LAYOUT);
716 mHandler.removeMessages(MyHandler.MSG_ON_WRITE);
717 mHandler.removeMessages(MyHandler.MSG_ON_FINISH);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700718 mHandler = null;
Svetoslava798c0a2014-05-15 10:47:19 -0700719
720 mObserver = null;
721
722 if (mPendingCallback != null) {
723 mPendingCallback.destroy();
724 mPendingCallback = null;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700725 }
726 }
727
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700728 private final class MyHandler extends Handler {
Svetoslava798c0a2014-05-15 10:47:19 -0700729 public static final int MSG_ON_START = 1;
730 public static final int MSG_ON_LAYOUT = 2;
731 public static final int MSG_ON_WRITE = 3;
732 public static final int MSG_ON_FINISH = 4;
Svet Ganovfce84f02014-10-31 16:56:52 -0700733 public static final int MSG_ON_KILL = 5;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700734
735 public MyHandler(Looper looper) {
736 super(looper, null, true);
737 }
738
739 @Override
740 public void handleMessage(Message message) {
741 switch (message.what) {
Svetoslava798c0a2014-05-15 10:47:19 -0700742 case MSG_ON_START: {
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700743 if (DEBUG) {
Svetoslava798c0a2014-05-15 10:47:19 -0700744 Log.i(LOG_TAG, "onStart()");
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700745 }
Svetoslava798c0a2014-05-15 10:47:19 -0700746
747 ((PrintDocumentAdapter) message.obj).onStart();
748 } break;
749
750 case MSG_ON_LAYOUT: {
751 SomeArgs args = (SomeArgs) message.obj;
752 PrintDocumentAdapter adapter = (PrintDocumentAdapter) args.arg1;
753 PrintAttributes oldAttributes = (PrintAttributes) args.arg2;
754 PrintAttributes newAttributes = (PrintAttributes) args.arg3;
755 CancellationSignal cancellation = (CancellationSignal) args.arg4;
756 LayoutResultCallback callback = (LayoutResultCallback) args.arg5;
757 Bundle metadata = (Bundle) args.arg6;
758 args.recycle();
759
760 if (DEBUG) {
761 StringBuilder builder = new StringBuilder();
762 builder.append("PrintDocumentAdapter#onLayout() {\n");
763 builder.append("\n oldAttributes:").append(oldAttributes);
764 builder.append("\n newAttributes:").append(newAttributes);
765 builder.append("\n preview:").append(metadata.getBoolean(
766 PrintDocumentAdapter.EXTRA_PRINT_PREVIEW));
767 builder.append("\n}");
768 Log.i(LOG_TAG, builder.toString());
769 }
770
771 adapter.onLayout(oldAttributes, newAttributes, cancellation,
772 callback, metadata);
773 } break;
774
775 case MSG_ON_WRITE: {
776 SomeArgs args = (SomeArgs) message.obj;
777 PrintDocumentAdapter adapter = (PrintDocumentAdapter) args.arg1;
778 PageRange[] pages = (PageRange[]) args.arg2;
779 ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg3;
780 CancellationSignal cancellation = (CancellationSignal) args.arg4;
781 WriteResultCallback callback = (WriteResultCallback) args.arg5;
782 args.recycle();
783
784 if (DEBUG) {
785 StringBuilder builder = new StringBuilder();
786 builder.append("PrintDocumentAdapter#onWrite() {\n");
787 builder.append("\n pages:").append(Arrays.toString(pages));
788 builder.append("\n}");
789 Log.i(LOG_TAG, builder.toString());
790 }
791
792 adapter.onWrite(pages, fd, cancellation, callback);
793 } break;
794
795 case MSG_ON_FINISH: {
796 if (DEBUG) {
797 Log.i(LOG_TAG, "onFinish()");
798 }
799
800 ((PrintDocumentAdapter) message.obj).onFinish();
801
802 // Done printing, so destroy this instance as it
803 // should not be used anymore.
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700804 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700805 destroyLocked();
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700806 }
807 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700808
Svet Ganovfce84f02014-10-31 16:56:52 -0700809 case MSG_ON_KILL: {
810 if (DEBUG) {
811 Log.i(LOG_TAG, "onKill()");
812 }
813
814 String reason = (String) message.obj;
815 throw new RuntimeException(reason);
816 }
817
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700818 default: {
819 throw new IllegalArgumentException("Unknown message: "
820 + message.what);
821 }
822 }
823 }
824 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700825
Svetoslava798c0a2014-05-15 10:47:19 -0700826 private interface DestroyableCallback {
827 public void destroy();
828 }
829
830 private final class MyLayoutResultCallback extends LayoutResultCallback
831 implements DestroyableCallback {
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700832 private ILayoutResultCallback mCallback;
833 private final int mSequence;
834
835 public MyLayoutResultCallback(ILayoutResultCallback callback,
836 int sequence) {
837 mCallback = callback;
838 mSequence = sequence;
839 }
840
841 @Override
842 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
843 final ILayoutResultCallback callback;
844 synchronized (mLock) {
845 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700846 }
Svetoslava798c0a2014-05-15 10:47:19 -0700847
848 // If the callback is null we are destroyed.
849 if (callback == null) {
850 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
851 + "finish the printing activity before print completion "
852 + "or did you invoke a callback after finish?");
853 return;
854 }
855
856 try {
857 if (info == null) {
858 throw new NullPointerException("document info cannot be null");
859 }
860
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700861 try {
862 callback.onLayoutFinished(info, changed, mSequence);
863 } catch (RemoteException re) {
864 Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
865 }
Svetoslava798c0a2014-05-15 10:47:19 -0700866 } finally {
867 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700868 }
869 }
870
871 @Override
872 public void onLayoutFailed(CharSequence error) {
873 final ILayoutResultCallback callback;
874 synchronized (mLock) {
875 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700876 }
Svetoslava798c0a2014-05-15 10:47:19 -0700877
878 // If the callback is null we are destroyed.
879 if (callback == null) {
880 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
881 + "finish the printing activity before print completion "
882 + "or did you invoke a callback after finish?");
883 return;
884 }
885
886 try {
887 callback.onLayoutFailed(error, mSequence);
888 } catch (RemoteException re) {
889 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
890 } finally {
891 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700892 }
893 }
894
895 @Override
896 public void onLayoutCancelled() {
Svetoslava798c0a2014-05-15 10:47:19 -0700897 final ILayoutResultCallback callback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700898 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700899 callback = mCallback;
900 }
901
902 // If the callback is null we are destroyed.
903 if (callback == null) {
904 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
905 + "finish the printing activity before print completion "
906 + "or did you invoke a callback after finish?");
907 return;
908 }
909
910 try {
911 callback.onLayoutCanceled(mSequence);
912 } catch (RemoteException re) {
913 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
914 } finally {
915 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700916 }
917 }
918
Svetoslava798c0a2014-05-15 10:47:19 -0700919 @Override
920 public void destroy() {
921 synchronized (mLock) {
922 mCallback = null;
923 mPendingCallback = null;
924 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700925 }
926 }
927
Svetoslava798c0a2014-05-15 10:47:19 -0700928 private final class MyWriteResultCallback extends WriteResultCallback
929 implements DestroyableCallback {
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700930 private ParcelFileDescriptor mFd;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700931 private IWriteResultCallback mCallback;
Svetoslava798c0a2014-05-15 10:47:19 -0700932 private final int mSequence;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700933
934 public MyWriteResultCallback(IWriteResultCallback callback,
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700935 ParcelFileDescriptor fd, int sequence) {
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700936 mFd = fd;
937 mSequence = sequence;
938 mCallback = callback;
939 }
940
941 @Override
942 public void onWriteFinished(PageRange[] pages) {
943 final IWriteResultCallback callback;
944 synchronized (mLock) {
945 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700946 }
Svetoslava798c0a2014-05-15 10:47:19 -0700947
948 // If the callback is null we are destroyed.
949 if (callback == null) {
950 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
951 + "finish the printing activity before print completion "
952 + "or did you invoke a callback after finish?");
953 return;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700954 }
Svetoslava798c0a2014-05-15 10:47:19 -0700955
956 try {
957 if (pages == null) {
958 throw new IllegalArgumentException("pages cannot be null");
959 }
960 if (pages.length == 0) {
961 throw new IllegalArgumentException("pages cannot be empty");
962 }
963
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700964 try {
965 callback.onWriteFinished(pages, mSequence);
966 } catch (RemoteException re) {
967 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
968 }
Svetoslava798c0a2014-05-15 10:47:19 -0700969 } finally {
970 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700971 }
972 }
973
974 @Override
975 public void onWriteFailed(CharSequence error) {
976 final IWriteResultCallback callback;
977 synchronized (mLock) {
978 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700979 }
Svetoslava798c0a2014-05-15 10:47:19 -0700980
981 // If the callback is null we are destroyed.
982 if (callback == null) {
983 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
984 + "finish the printing activity before print completion "
985 + "or did you invoke a callback after finish?");
986 return;
987 }
988
989 try {
990 callback.onWriteFailed(error, mSequence);
991 } catch (RemoteException re) {
992 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
993 } finally {
994 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700995 }
996 }
997
998 @Override
999 public void onWriteCancelled() {
Svetoslava798c0a2014-05-15 10:47:19 -07001000 final IWriteResultCallback callback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001001 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -07001002 callback = mCallback;
1003 }
1004
1005 // If the callback is null we are destroyed.
1006 if (callback == null) {
1007 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1008 + "finish the printing activity before print completion "
1009 + "or did you invoke a callback after finish?");
1010 return;
1011 }
1012
1013 try {
1014 callback.onWriteCanceled(mSequence);
1015 } catch (RemoteException re) {
1016 Log.e(LOG_TAG, "Error calling onWriteCanceled", re);
1017 } finally {
1018 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001019 }
1020 }
1021
Svetoslava798c0a2014-05-15 10:47:19 -07001022 @Override
1023 public void destroy() {
1024 synchronized (mLock) {
1025 IoUtils.closeQuietly(mFd);
1026 mCallback = null;
1027 mFd = null;
1028 mPendingCallback = null;
1029 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001030 }
1031 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001032 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001033
1034 private static final class PrintJobStateChangeListenerWrapper extends
1035 IPrintJobStateChangeListener.Stub {
1036 private final WeakReference<PrintJobStateChangeListener> mWeakListener;
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001037 private final WeakReference<Handler> mWeakHandler;
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001038
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001039 public PrintJobStateChangeListenerWrapper(PrintJobStateChangeListener listener,
1040 Handler handler) {
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001041 mWeakListener = new WeakReference<PrintJobStateChangeListener>(listener);
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001042 mWeakHandler = new WeakReference<Handler>(handler);
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001043 }
1044
1045 @Override
1046 public void onPrintJobStateChanged(PrintJobId printJobId) {
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001047 Handler handler = mWeakHandler.get();
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001048 PrintJobStateChangeListener listener = mWeakListener.get();
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001049 if (handler != null && listener != null) {
1050 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -07001051 args.arg1 = this;
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001052 args.arg2 = printJobId;
1053 handler.obtainMessage(MSG_NOTIFY_PRINT_JOB_STATE_CHANGED,
1054 args).sendToTarget();
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001055 }
1056 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001057
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -07001058 public void destroy() {
1059 mWeakListener.clear();
1060 }
1061
1062 public PrintJobStateChangeListener getListener() {
1063 return mWeakListener.get();
1064 }
1065 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001066}