blob: be9b59658c7bec63be67a8675070b19d18ed6e5c [file] [log] [blame]
/*
* 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.PrintResultCallback;
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.
* <p>
* To obtain a handle to the print manager do the following:
* </p>
* <pre>
* PrintManager printManager =
* (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
* </pre>
*/
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<PrintJob> getPrintJobs() {
try {
List<PrintJobInfo> printJobInfos = mService.getPrintJobs(mAppId, mUserId);
if (printJobInfos == null) {
return Collections.emptyList();
}
final int printJobCount = printJobInfos.size();
List<PrintJob> printJobs = new ArrayList<PrintJob>(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.
*
* @see PrintJob
*/
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.
*
* @see PrintJob
*/
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<PrintManager> mWeakPrintManager;
public PrintClient(PrintManager manager) {
mWeakPrintManager = new WeakReference<PrintManager>(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<PageRange> pages, ParcelFileDescriptor fd,
IPrintResultCallback callback) {
synchronized (mLock) {
if (isFinishedLocked()) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = mPrintAdapter;
args.arg2 = pages;
args.arg3 = fd.getFileDescriptor();
args.arg4 = callback;
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<PageRange> pages = (List<PageRange>) args.arg2;
final FileDescriptor fd = (FileDescriptor) args.arg3;
IPrintResultCallback callback = (IPrintResultCallback) args.arg4;
args.recycle();
try {
ICancellationSignal remoteSignal = CancellationSignal.createTransport();
callback.onPrintStarted(adapter.getInfo(), remoteSignal);
CancellationSignal localSignal = CancellationSignal.fromTransport(
remoteSignal);
adapter.onPrint(pages, fd, localSignal,
new PrintResultCallbackWrapper(callback) {
@Override
public void onPrintFinished(List<PageRange> 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 PrintResultCallbackWrapper extends PrintResultCallback {
private final IPrintResultCallback mWrappedCallback;
public PrintResultCallbackWrapper(IPrintResultCallback callback) {
mWrappedCallback = callback;
}
@Override
public void onPrintFinished(List<PageRange> pages) {
try {
mWrappedCallback.onPrintFinished(pages);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error calling onPrintFinished", re);
}
}
@Override
public void onPrintFailed(CharSequence error) {
try {
mWrappedCallback.onPrintFailed(error);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error calling onPrintFailed", re);
}
}
}
}