blob: 58f45fa1fcf1c004405ec590a3ab63e143b7549e [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
19import android.content.Context;
20import android.content.IntentSender;
21import android.content.IntentSender.SendIntentException;
Svetoslav62836082013-07-17 14:52:35 -070022import android.os.Bundle;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070023import android.os.CancellationSignal;
24import android.os.Handler;
25import android.os.ICancellationSignal;
26import android.os.Looper;
27import android.os.Message;
28import android.os.ParcelFileDescriptor;
29import android.os.RemoteException;
Svetoslav Ganova0027152013-06-25 14:59:53 -070030import android.print.PrintDocumentAdapter.LayoutResultCallback;
31import android.print.PrintDocumentAdapter.WriteResultCallback;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070032import android.util.Log;
33
34import com.android.internal.os.SomeArgs;
35
36import libcore.io.IoUtils;
37
38import java.io.File;
39import java.io.FileDescriptor;
40import java.lang.ref.WeakReference;
41import java.util.ArrayList;
42import java.util.Collections;
43import java.util.List;
44
45/**
46 * System level service for accessing the printing capabilities of the platform.
47 * <p>
48 * To obtain a handle to the print manager do the following:
49 * </p>
50 * <pre>
51 * PrintManager printManager =
52 * (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
53 * </pre>
54 */
55public final class PrintManager {
56
57 private static final String LOG_TAG = "PrintManager";
58
59 /** @hide */
60 public static final int APP_ID_ANY = -2;
61
62 private final Context mContext;
63
64 private final IPrintManager mService;
65
66 private final int mUserId;
67
68 private final int mAppId;
69
70 private final PrintClient mPrintClient;
71
72 private final Handler mHandler;
73
74 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070075 * Creates a new instance.
76 *
77 * @param context The current context in which to operate.
78 * @param service The backing system service.
79 *
80 * @hide
81 */
82 public PrintManager(Context context, IPrintManager service, int userId, int appId) {
83 mContext = context;
84 mService = service;
85 mUserId = userId;
86 mAppId = appId;
87 mPrintClient = new PrintClient(this);
88 mHandler = new Handler(context.getMainLooper(), null, false) {
89 @Override
90 public void handleMessage(Message message) {
91 SomeArgs args = (SomeArgs) message.obj;
92 Context context = (Context) args.arg1;
93 IntentSender intent = (IntentSender) args.arg2;
94 args.recycle();
95 try {
96 context.startIntentSender(intent, null, 0, 0, 0);
97 } catch (SendIntentException sie) {
98 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
99 }
100 }
101 };
102 }
103
104 /**
105 * Creates an instance that can access all print jobs.
106 *
107 * @param userId The user id for which to get all print jobs.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700108 * @return An instance if the caller has the permission to access
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700109 * all print jobs, null otherwise.
110 *
111 * @hide
112 */
113 public PrintManager getGlobalPrintManagerForUser(int userId) {
114 return new PrintManager(mContext, mService, userId, APP_ID_ANY);
115 }
116
Svetoslav Ganova0027152013-06-25 14:59:53 -0700117 PrintJobInfo getPrintJobInfo(int printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700118 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700119 return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700120 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700121 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700122 }
123 return null;
124 }
125
126 /**
127 * Gets the print jobs for this application.
128 *
129 * @return The print job list.
130 *
131 * @see PrintJob
132 */
133 public List<PrintJob> getPrintJobs() {
134 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700135 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700136 if (printJobInfos == null) {
137 return Collections.emptyList();
138 }
139 final int printJobCount = printJobInfos.size();
140 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
141 for (int i = 0; i < printJobCount; i++) {
142 printJobs.add(new PrintJob(printJobInfos.get(i), this));
143 }
144 return printJobs;
145 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700146 Log.e(LOG_TAG, "Error getting print jobs", re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700147 }
148 return Collections.emptyList();
149 }
150
Svetoslav Ganova0027152013-06-25 14:59:53 -0700151 void cancelPrintJob(int printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700152 try {
153 mService.cancelPrintJob(printJobId, mAppId, mUserId);
154 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700155 Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700156 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700157 }
158
159 /**
160 * Creates a print job for printing a file with default print attributes.
161 *
162 * @param printJobName A name for the new print job.
163 * @param pdfFile The PDF file to print.
164 * @param attributes The default print job attributes.
165 * @return The created print job.
Svetoslavfd906512013-06-24 09:04:48 -0700166 *
167 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700168 */
169 public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
Svetoslav17b7f6e2013-06-24 18:29:33 -0700170 FileDocumentAdapter documentAdapter = new FileDocumentAdapter(mContext, pdfFile);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700171 return print(printJobName, documentAdapter, attributes);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700172 }
173
174 /**
Svetoslav Ganova0027152013-06-25 14:59:53 -0700175 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700176 * attributes.
177 *
178 * @param printJobName A name for the new print job.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700179 * @param documentAdapter An adapter that emits the document to print.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700180 * @param attributes The default print job attributes.
181 * @return The created print job.
Svetoslavfd906512013-06-24 09:04:48 -0700182 *
183 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700184 */
Svetoslav Ganova0027152013-06-25 14:59:53 -0700185 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700186 PrintAttributes attributes) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700187 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700188 mContext.getMainLooper());
189 try {
190 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
191 attributes, mAppId, mUserId);
192 if (printJob != null) {
193 return new PrintJob(printJob, this);
194 }
195 } catch (RemoteException re) {
196 Log.e(LOG_TAG, "Error creating a print job", re);
197 }
198 return null;
199 }
200
201 private static final class PrintClient extends IPrintClient.Stub {
202
203 private final WeakReference<PrintManager> mWeakPrintManager;
204
205 public PrintClient(PrintManager manager) {
206 mWeakPrintManager = new WeakReference<PrintManager>(manager);
207 }
208
209 @Override
210 public void startPrintJobConfigActivity(IntentSender intent) {
211 PrintManager manager = mWeakPrintManager.get();
212 if (manager != null) {
213 SomeArgs args = SomeArgs.obtain();
214 args.arg1 = manager.mContext;
215 args.arg2 = intent;
216 manager.mHandler.obtainMessage(0, args).sendToTarget();
217 }
218 }
219 }
220
Svetoslav Ganova0027152013-06-25 14:59:53 -0700221 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
222 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700223
Svetoslav Ganova0027152013-06-25 14:59:53 -0700224 private Handler mHandler; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700225
Svetoslav Ganova0027152013-06-25 14:59:53 -0700226 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
227 mDocumentAdapter = documentAdapter;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700228 mHandler = new MyHandler(looper);
229 }
230
231 @Override
232 public void start() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700233 mHandler.sendEmptyMessage(MyHandler.MSG_START);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700234 }
235
236 @Override
Svetoslav62836082013-07-17 14:52:35 -0700237 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
238 ILayoutResultCallback callback, Bundle metadata) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700239 SomeArgs args = SomeArgs.obtain();
240 args.arg1 = oldAttributes;
241 args.arg2 = newAttributes;
242 args.arg3 = callback;
Svetoslav62836082013-07-17 14:52:35 -0700243 args.arg4 = metadata;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700244 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700245 }
246
247 @Override
Svetoslav Ganova0027152013-06-25 14:59:53 -0700248 public void write(List<PageRange> pages, ParcelFileDescriptor fd,
249 IWriteResultCallback callback) {
250 SomeArgs args = SomeArgs.obtain();
251 args.arg1 = pages;
252 args.arg2 = fd.getFileDescriptor();
253 args.arg3 = callback;
254 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700255 }
256
257 @Override
258 public void finish() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700259 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700260 }
261
Svetoslav Ganova0027152013-06-25 14:59:53 -0700262 private boolean isFinished() {
263 return mDocumentAdapter == null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700264 }
265
Svetoslav Ganova0027152013-06-25 14:59:53 -0700266 private void doFinish() {
267 mDocumentAdapter = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700268 mHandler = null;
269 }
270
271 private final class MyHandler extends Handler {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700272 public static final int MSG_START = 1;
273 public static final int MSG_LAYOUT = 2;
274 public static final int MSG_WRITE = 3;
275 public static final int MSG_FINISH = 4;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700276
277 public MyHandler(Looper looper) {
278 super(looper, null, true);
279 }
280
281 @Override
Svetoslav Ganova0027152013-06-25 14:59:53 -0700282 @SuppressWarnings("unchecked")
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700283 public void handleMessage(Message message) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700284 if (isFinished()) {
285 return;
286 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700287 switch (message.what) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700288 case MSG_START: {
289 mDocumentAdapter.onStart();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700290 } break;
291
Svetoslav Ganova0027152013-06-25 14:59:53 -0700292 case MSG_LAYOUT: {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700293 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700294 PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
295 PrintAttributes newAttributes = (PrintAttributes) args.arg2;
296 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
Svetoslav62836082013-07-17 14:52:35 -0700297 Bundle metadata = (Bundle) args.arg4;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700298 args.recycle();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700299
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700300 try {
301 ICancellationSignal remoteSignal = CancellationSignal.createTransport();
Svetoslav Ganova0027152013-06-25 14:59:53 -0700302 callback.onLayoutStarted(remoteSignal);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700303
Svetoslav Ganova0027152013-06-25 14:59:53 -0700304 mDocumentAdapter.onLayout(oldAttributes, newAttributes,
305 CancellationSignal.fromTransport(remoteSignal),
Svetoslav62836082013-07-17 14:52:35 -0700306 new LayoutResultCallbackWrapper(callback), metadata);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700307 } catch (RemoteException re) {
308 Log.e(LOG_TAG, "Error printing", re);
309 }
310 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700311
Svetoslav Ganova0027152013-06-25 14:59:53 -0700312 case MSG_WRITE: {
313 SomeArgs args = (SomeArgs) message.obj;
314 List<PageRange> pages = (List<PageRange>) args.arg1;
315 FileDescriptor fd = (FileDescriptor) args.arg2;
316 IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
317 args.recycle();
318
319 try {
320 ICancellationSignal remoteSignal = CancellationSignal.createTransport();
321 callback.onWriteStarted(remoteSignal);
322
323 mDocumentAdapter.onWrite(pages, fd,
324 CancellationSignal.fromTransport(remoteSignal),
325 new WriteResultCallbackWrapper(callback, fd));
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700326 } catch (RemoteException re) {
327 Log.e(LOG_TAG, "Error printing", re);
328 IoUtils.closeQuietly(fd);
329 }
330 } break;
331
Svetoslav Ganova0027152013-06-25 14:59:53 -0700332 case MSG_FINISH: {
333 mDocumentAdapter.onFinish();
334 doFinish();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700335 } break;
336
337 default: {
338 throw new IllegalArgumentException("Unknown message: "
339 + message.what);
340 }
341 }
342 }
343 }
344 }
345
Svetoslav Ganova0027152013-06-25 14:59:53 -0700346 private static final class WriteResultCallbackWrapper extends WriteResultCallback {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700347
Svetoslav Ganova0027152013-06-25 14:59:53 -0700348 private final IWriteResultCallback mWrappedCallback;
349 private final FileDescriptor mFd;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700350
Svetoslav Ganova0027152013-06-25 14:59:53 -0700351 public WriteResultCallbackWrapper(IWriteResultCallback callback,
352 FileDescriptor fd) {
Svetoslavfd906512013-06-24 09:04:48 -0700353 mWrappedCallback = callback;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700354 mFd = fd;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700355 }
356
357 @Override
Svetoslav Ganova0027152013-06-25 14:59:53 -0700358 public void onWriteFinished(List<PageRange> pages) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700359 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700360 // Close before notifying the other end. We want
361 // to be ready by the time we announce it.
362 IoUtils.closeQuietly(mFd);
363 mWrappedCallback.onWriteFinished(pages);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700364 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700365 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700366 }
367 }
368
369 @Override
Svetoslav Ganova0027152013-06-25 14:59:53 -0700370 public void onWriteFailed(CharSequence error) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700371 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700372 // Close before notifying the other end. We want
373 // to be ready by the time we announce it.
374 IoUtils.closeQuietly(mFd);
375 mWrappedCallback.onWriteFailed(error);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700376 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700377 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
378 }
379 }
380 }
381
382 private static final class LayoutResultCallbackWrapper extends LayoutResultCallback {
383
384 private final ILayoutResultCallback mWrappedCallback;
385
386 public LayoutResultCallbackWrapper(ILayoutResultCallback callback) {
387 mWrappedCallback = callback;
388 }
389
390 @Override
391 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
392 try {
393 mWrappedCallback.onLayoutFinished(info, changed);
394 } catch (RemoteException re) {
395 Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
396 }
397 }
398
399 @Override
400 public void onLayoutFailed(CharSequence error) {
401 try {
402 mWrappedCallback.onLayoutFailed(error);
403 } catch (RemoteException re) {
404 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700405 }
406 }
407 }
408}