blob: df14a5c6c9860f5d7d7b5ea49468a437e07ca980 [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;
22import android.os.CancellationSignal;
23import android.os.Handler;
24import android.os.ICancellationSignal;
25import android.os.Looper;
26import android.os.Message;
27import android.os.ParcelFileDescriptor;
28import android.os.RemoteException;
29import android.print.PrintAdapter.PrintProgressCallback;
30import android.util.Log;
31
32import com.android.internal.os.SomeArgs;
33
34import libcore.io.IoUtils;
35
36import java.io.File;
37import java.io.FileDescriptor;
38import java.lang.ref.WeakReference;
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.List;
42
43/**
44 * System level service for accessing the printing capabilities of the platform.
45 * <p>
46 * To obtain a handle to the print manager do the following:
47 * </p>
48 * <pre>
49 * PrintManager printManager =
50 * (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
51 * </pre>
52 */
53public final class PrintManager {
54
55 private static final String LOG_TAG = "PrintManager";
56
57 /** @hide */
58 public static final int APP_ID_ANY = -2;
59
60 private final Context mContext;
61
62 private final IPrintManager mService;
63
64 private final int mUserId;
65
66 private final int mAppId;
67
68 private final PrintClient mPrintClient;
69
70 private final Handler mHandler;
71
72 /**
73 * Listener for the state of a print job.
74 */
75 public static interface PrintJobStateListener {
76 public void onStateChanged(int state);
77 }
78
79 /**
80 * Creates a new instance.
81 *
82 * @param context The current context in which to operate.
83 * @param service The backing system service.
84 *
85 * @hide
86 */
87 public PrintManager(Context context, IPrintManager service, int userId, int appId) {
88 mContext = context;
89 mService = service;
90 mUserId = userId;
91 mAppId = appId;
92 mPrintClient = new PrintClient(this);
93 mHandler = new Handler(context.getMainLooper(), null, false) {
94 @Override
95 public void handleMessage(Message message) {
96 SomeArgs args = (SomeArgs) message.obj;
97 Context context = (Context) args.arg1;
98 IntentSender intent = (IntentSender) args.arg2;
99 args.recycle();
100 try {
101 context.startIntentSender(intent, null, 0, 0, 0);
102 } catch (SendIntentException sie) {
103 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
104 }
105 }
106 };
107 }
108
109 /**
110 * Creates an instance that can access all print jobs.
111 *
112 * @param userId The user id for which to get all print jobs.
113 * @return An instance of the caller has the permission to access
114 * all print jobs, null otherwise.
115 *
116 * @hide
117 */
118 public PrintManager getGlobalPrintManagerForUser(int userId) {
119 return new PrintManager(mContext, mService, userId, APP_ID_ANY);
120 }
121
122 PrintJobInfo getPrintJob(int printJobId) {
123 try {
124 return mService.getPrintJob(printJobId, mAppId, mUserId);
125 } catch (RemoteException re) {
126 Log.e(LOG_TAG, "Error getting print job:" + printJobId, re);
127 }
128 return null;
129 }
130
131 /**
132 * Gets the print jobs for this application.
133 *
134 * @return The print job list.
135 *
136 * @see PrintJob
137 */
138 public List<PrintJob> getPrintJobs() {
139 try {
140 List<PrintJobInfo> printJobInfos = mService.getPrintJobs(mAppId, mUserId);
141 if (printJobInfos == null) {
142 return Collections.emptyList();
143 }
144 final int printJobCount = printJobInfos.size();
145 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
146 for (int i = 0; i < printJobCount; i++) {
147 printJobs.add(new PrintJob(printJobInfos.get(i), this));
148 }
149 return printJobs;
150 } catch (RemoteException re) {
151 Log.e(LOG_TAG, "Error getting print jobs!", re);
152 }
153 return Collections.emptyList();
154 }
155
156 ICancellationSignal cancelPrintJob(int printJobId) {
157 try {
158 mService.cancelPrintJob(printJobId, mAppId, mUserId);
159 } catch (RemoteException re) {
160 Log.e(LOG_TAG, "Error cancleing a print job:" + printJobId, re);
161 }
162 return null;
163 }
164
165 /**
166 * Creates a print job for printing a file with default print attributes.
167 *
168 * @param printJobName A name for the new print job.
169 * @param pdfFile The PDF file to print.
170 * @param attributes The default print job attributes.
171 * @return The created print job.
172 */
173 public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
174 PrintFileAdapter printable = new PrintFileAdapter(pdfFile);
175 return print(printJobName, printable, attributes);
176 }
177
178 /**
179 * Creates a print job for printing a {@link PrintAdapter} with default print
180 * attributes.
181 *
182 * @param printJobName A name for the new print job.
183 * @param printAdapter The printable adapter to print.
184 * @param attributes The default print job attributes.
185 * @return The created print job.
186 */
187 public PrintJob print(String printJobName, PrintAdapter printAdapter,
188 PrintAttributes attributes) {
189 PrintAdapterDelegate delegate = new PrintAdapterDelegate(printAdapter,
190 mContext.getMainLooper());
191 try {
192 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
193 attributes, mAppId, mUserId);
194 if (printJob != null) {
195 return new PrintJob(printJob, this);
196 }
197 } catch (RemoteException re) {
198 Log.e(LOG_TAG, "Error creating a print job", re);
199 }
200 return null;
201 }
202
203 private static final class PrintClient extends IPrintClient.Stub {
204
205 private final WeakReference<PrintManager> mWeakPrintManager;
206
207 public PrintClient(PrintManager manager) {
208 mWeakPrintManager = new WeakReference<PrintManager>(manager);
209 }
210
211 @Override
212 public void startPrintJobConfigActivity(IntentSender intent) {
213 PrintManager manager = mWeakPrintManager.get();
214 if (manager != null) {
215 SomeArgs args = SomeArgs.obtain();
216 args.arg1 = manager.mContext;
217 args.arg2 = intent;
218 manager.mHandler.obtainMessage(0, args).sendToTarget();
219 }
220 }
221 }
222
223 private static final class PrintAdapterDelegate extends IPrintAdapter.Stub {
224 private final Object mLock = new Object();
225
226 private PrintAdapter mPrintAdapter;
227
228 private Handler mHandler;
229
230 public PrintAdapterDelegate(PrintAdapter printAdapter, Looper looper) {
231 mPrintAdapter = printAdapter;
232 mHandler = new MyHandler(looper);
233 }
234
235 @Override
236 public void start() {
237 synchronized (mLock) {
238 if (isFinishedLocked()) {
239 return;
240 }
241 mHandler.obtainMessage(MyHandler.MESSAGE_START,
242 mPrintAdapter).sendToTarget();
243 }
244 }
245
246 @Override
247 public void printAttributesChanged(PrintAttributes attributes) {
248 synchronized (mLock) {
249 if (isFinishedLocked()) {
250 return;
251 }
252 SomeArgs args = SomeArgs.obtain();
253 args.arg1 = mPrintAdapter;
254 args.arg2 = attributes;
255 mHandler.obtainMessage(MyHandler.MESSAGE_PRINT_ATTRIBUTES_CHANGED,
256 args).sendToTarget();
257 }
258 }
259
260 @Override
261 public void print(List<PageRange> pages, ParcelFileDescriptor fd,
262 IPrintProgressListener progressListener) {
263 synchronized (mLock) {
264 if (isFinishedLocked()) {
265 return;
266 }
267 SomeArgs args = SomeArgs.obtain();
268 args.arg1 = mPrintAdapter;
269 args.arg2 = pages;
270 args.arg3 = fd.getFileDescriptor();
271 args.arg4 = progressListener;
272 mHandler.obtainMessage(MyHandler.MESSAGE_PRINT, args).sendToTarget();
273 }
274 }
275
276 @Override
277 public void finish() {
278 synchronized (mLock) {
279 if (isFinishedLocked()) {
280 return;
281 }
282 mHandler.obtainMessage(MyHandler.MESSAGE_FINIS,
283 mPrintAdapter).sendToTarget();
284 }
285 }
286
287 private boolean isFinishedLocked() {
288 return mPrintAdapter == null;
289 }
290
291 private void finishLocked() {
292 mPrintAdapter = null;
293 mHandler = null;
294 }
295
296 private final class MyHandler extends Handler {
297 public static final int MESSAGE_START = 1;
298 public static final int MESSAGE_PRINT_ATTRIBUTES_CHANGED = 2;
299 public static final int MESSAGE_PRINT = 3;
300 public static final int MESSAGE_FINIS = 4;
301
302 public MyHandler(Looper looper) {
303 super(looper, null, true);
304 }
305
306 @Override
307 public void handleMessage(Message message) {
308 switch (message.what) {
309 case MESSAGE_START: {
310 PrintAdapter adapter = (PrintAdapter) message.obj;
311 adapter.onStart();
312 } break;
313
314 case MESSAGE_PRINT_ATTRIBUTES_CHANGED: {
315 SomeArgs args = (SomeArgs) message.obj;
316 PrintAdapter adapter = (PrintAdapter) args.arg1;
317 PrintAttributes attributes = (PrintAttributes) args.arg2;
318 args.recycle();
319 adapter.onPrintAttributesChanged(attributes);
320 } break;
321
322 case MESSAGE_PRINT: {
323 SomeArgs args = (SomeArgs) message.obj;
324 PrintAdapter adapter = (PrintAdapter) args.arg1;
325 @SuppressWarnings("unchecked")
326 List<PageRange> pages = (List<PageRange>) args.arg2;
327 final FileDescriptor fd = (FileDescriptor) args.arg3;
328 IPrintProgressListener listener = (IPrintProgressListener) args.arg4;
329 args.recycle();
330 try {
331 ICancellationSignal remoteSignal = CancellationSignal.createTransport();
332 listener.onWriteStarted(adapter.getInfo(), remoteSignal);
333
334 CancellationSignal localSignal = CancellationSignal.fromTransport(
335 remoteSignal);
336 adapter.onPrint(pages, fd, localSignal,
337 new PrintProgressListenerWrapper(listener) {
338 @Override
339 public void onPrintFinished(List<PageRange> pages) {
340 IoUtils.closeQuietly(fd);
341 super.onPrintFinished(pages);
342 }
343
344 @Override
345 public void onPrintFailed(CharSequence error) {
346 IoUtils.closeQuietly(fd);
347 super.onPrintFailed(error);
348 }
349 });
350 } catch (RemoteException re) {
351 Log.e(LOG_TAG, "Error printing", re);
352 IoUtils.closeQuietly(fd);
353 }
354 } break;
355
356 case MESSAGE_FINIS: {
357 PrintAdapter adapter = (PrintAdapter) message.obj;
358 adapter.onFinish();
359 synchronized (mLock) {
360 finishLocked();
361 }
362 } break;
363
364 default: {
365 throw new IllegalArgumentException("Unknown message: "
366 + message.what);
367 }
368 }
369 }
370 }
371 }
372
373 private static abstract class PrintProgressListenerWrapper extends PrintProgressCallback {
374
375 private final IPrintProgressListener mWrappedListener;
376
377 public PrintProgressListenerWrapper(IPrintProgressListener listener) {
378 mWrappedListener = listener;
379 }
380
381 @Override
382 public void onPrintFinished(List<PageRange> pages) {
383 try {
384 mWrappedListener.onWriteFinished(pages);
385 } catch (RemoteException re) {
386 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
387 }
388 }
389
390 @Override
391 public void onPrintFailed(CharSequence error) {
392 try {
393 mWrappedListener.onWriteFailed(error);
394 } catch (RemoteException re) {
395 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
396 }
397 }
398 }
399}