PrintManager.java revision 4b9a4d16872bbb50712e007b419ac0b35ff1582d
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.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}
400