/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.print; import android.content.Context; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.os.CancellationSignal; import android.os.Handler; import android.os.ICancellationSignal; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.print.PrintAdapter.PrintProgressCallback; import android.util.Log; import com.android.internal.os.SomeArgs; import libcore.io.IoUtils; import java.io.File; import java.io.FileDescriptor; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * System level service for accessing the printing capabilities of the platform. *

* To obtain a handle to the print manager do the following: *

*
 * PrintManager printManager =
 *         (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
 * 
*/ public final class PrintManager { private static final String LOG_TAG = "PrintManager"; /** @hide */ public static final int APP_ID_ANY = -2; private final Context mContext; private final IPrintManager mService; private final int mUserId; private final int mAppId; private final PrintClient mPrintClient; private final Handler mHandler; /** * Creates a new instance. * * @param context The current context in which to operate. * @param service The backing system service. * * @hide */ public PrintManager(Context context, IPrintManager service, int userId, int appId) { mContext = context; mService = service; mUserId = userId; mAppId = appId; mPrintClient = new PrintClient(this); mHandler = new Handler(context.getMainLooper(), null, false) { @Override public void handleMessage(Message message) { SomeArgs args = (SomeArgs) message.obj; Context context = (Context) args.arg1; IntentSender intent = (IntentSender) args.arg2; args.recycle(); try { context.startIntentSender(intent, null, 0, 0, 0); } catch (SendIntentException sie) { Log.e(LOG_TAG, "Couldn't start print job config activity.", sie); } } }; } /** * Creates an instance that can access all print jobs. * * @param userId The user id for which to get all print jobs. * @return An instance of the caller has the permission to access * all print jobs, null otherwise. * * @hide */ public PrintManager getGlobalPrintManagerForUser(int userId) { return new PrintManager(mContext, mService, userId, APP_ID_ANY); } PrintJobInfo getPrintJob(int printJobId) { try { return mService.getPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error getting print job:" + printJobId, re); } return null; } /** * Gets the print jobs for this application. * * @return The print job list. * * @see PrintJob */ public List getPrintJobs() { try { List printJobInfos = mService.getPrintJobs(mAppId, mUserId); if (printJobInfos == null) { return Collections.emptyList(); } final int printJobCount = printJobInfos.size(); List printJobs = new ArrayList(printJobCount); for (int i = 0; i < printJobCount; i++) { printJobs.add(new PrintJob(printJobInfos.get(i), this)); } return printJobs; } catch (RemoteException re) { Log.e(LOG_TAG, "Error getting print jobs!", re); } return Collections.emptyList(); } ICancellationSignal cancelPrintJob(int printJobId) { try { mService.cancelPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error cancleing a print job:" + printJobId, re); } return null; } /** * Creates a print job for printing a file with default print attributes. * * @param printJobName A name for the new print job. * @param pdfFile The PDF file to print. * @param attributes The default print job attributes. * @return The created print job. */ public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) { PrintFileAdapter printable = new PrintFileAdapter(pdfFile); return print(printJobName, printable, attributes); } /** * Creates a print job for printing a {@link PrintAdapter} with default print * attributes. * * @param printJobName A name for the new print job. * @param printAdapter The printable adapter to print. * @param attributes The default print job attributes. * @return The created print job. */ public PrintJob print(String printJobName, PrintAdapter printAdapter, PrintAttributes attributes) { PrintAdapterDelegate delegate = new PrintAdapterDelegate(printAdapter, mContext.getMainLooper()); try { PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate, attributes, mAppId, mUserId); if (printJob != null) { return new PrintJob(printJob, this); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error creating a print job", re); } return null; } private static final class PrintClient extends IPrintClient.Stub { private final WeakReference mWeakPrintManager; public PrintClient(PrintManager manager) { mWeakPrintManager = new WeakReference(manager); } @Override public void startPrintJobConfigActivity(IntentSender intent) { PrintManager manager = mWeakPrintManager.get(); if (manager != null) { SomeArgs args = SomeArgs.obtain(); args.arg1 = manager.mContext; args.arg2 = intent; manager.mHandler.obtainMessage(0, args).sendToTarget(); } } } private static final class PrintAdapterDelegate extends IPrintAdapter.Stub { private final Object mLock = new Object(); private PrintAdapter mPrintAdapter; private Handler mHandler; public PrintAdapterDelegate(PrintAdapter printAdapter, Looper looper) { mPrintAdapter = printAdapter; mHandler = new MyHandler(looper); } @Override public void start() { synchronized (mLock) { if (isFinishedLocked()) { return; } mHandler.obtainMessage(MyHandler.MESSAGE_START, mPrintAdapter).sendToTarget(); } } @Override public void printAttributesChanged(PrintAttributes attributes) { synchronized (mLock) { if (isFinishedLocked()) { return; } SomeArgs args = SomeArgs.obtain(); args.arg1 = mPrintAdapter; args.arg2 = attributes; mHandler.obtainMessage(MyHandler.MESSAGE_PRINT_ATTRIBUTES_CHANGED, args).sendToTarget(); } } @Override public void print(List pages, ParcelFileDescriptor fd, IPrintProgressListener progressListener) { synchronized (mLock) { if (isFinishedLocked()) { return; } SomeArgs args = SomeArgs.obtain(); args.arg1 = mPrintAdapter; args.arg2 = pages; args.arg3 = fd.getFileDescriptor(); args.arg4 = progressListener; mHandler.obtainMessage(MyHandler.MESSAGE_PRINT, args).sendToTarget(); } } @Override public void finish() { synchronized (mLock) { if (isFinishedLocked()) { return; } mHandler.obtainMessage(MyHandler.MESSAGE_FINIS, mPrintAdapter).sendToTarget(); } } private boolean isFinishedLocked() { return mPrintAdapter == null; } private void finishLocked() { mPrintAdapter = null; mHandler = null; } private final class MyHandler extends Handler { public static final int MESSAGE_START = 1; public static final int MESSAGE_PRINT_ATTRIBUTES_CHANGED = 2; public static final int MESSAGE_PRINT = 3; public static final int MESSAGE_FINIS = 4; public MyHandler(Looper looper) { super(looper, null, true); } @Override public void handleMessage(Message message) { switch (message.what) { case MESSAGE_START: { PrintAdapter adapter = (PrintAdapter) message.obj; adapter.onStart(); } break; case MESSAGE_PRINT_ATTRIBUTES_CHANGED: { SomeArgs args = (SomeArgs) message.obj; PrintAdapter adapter = (PrintAdapter) args.arg1; PrintAttributes attributes = (PrintAttributes) args.arg2; args.recycle(); adapter.onPrintAttributesChanged(attributes); } break; case MESSAGE_PRINT: { SomeArgs args = (SomeArgs) message.obj; PrintAdapter adapter = (PrintAdapter) args.arg1; @SuppressWarnings("unchecked") List pages = (List) args.arg2; final FileDescriptor fd = (FileDescriptor) args.arg3; IPrintProgressListener listener = (IPrintProgressListener) args.arg4; args.recycle(); try { ICancellationSignal remoteSignal = CancellationSignal.createTransport(); listener.onWriteStarted(adapter.getInfo(), remoteSignal); CancellationSignal localSignal = CancellationSignal.fromTransport( remoteSignal); adapter.onPrint(pages, fd, localSignal, new PrintProgressListenerWrapper(listener) { @Override public void onPrintFinished(List pages) { IoUtils.closeQuietly(fd); super.onPrintFinished(pages); } @Override public void onPrintFailed(CharSequence error) { IoUtils.closeQuietly(fd); super.onPrintFailed(error); } }); } catch (RemoteException re) { Log.e(LOG_TAG, "Error printing", re); IoUtils.closeQuietly(fd); } } break; case MESSAGE_FINIS: { PrintAdapter adapter = (PrintAdapter) message.obj; adapter.onFinish(); synchronized (mLock) { finishLocked(); } } break; default: { throw new IllegalArgumentException("Unknown message: " + message.what); } } } } } private static abstract class PrintProgressListenerWrapper extends PrintProgressCallback { private final IPrintProgressListener mWrappedListener; public PrintProgressListenerWrapper(IPrintProgressListener listener) { mWrappedListener = listener; } @Override public void onPrintFinished(List pages) { try { mWrappedListener.onWriteFinished(pages); } catch (RemoteException re) { Log.e(LOG_TAG, "Error calling onWriteFinished", re); } } @Override public void onPrintFailed(CharSequence error) { try { mWrappedListener.onWriteFailed(error); } catch (RemoteException re) { Log.e(LOG_TAG, "Error calling onWriteFailed", re); } } } }