diff options
Diffstat (limited to 'core/java/android/printservice')
-rw-r--r-- | core/java/android/printservice/IPrintService.aidl | 35 | ||||
-rw-r--r-- | core/java/android/printservice/IPrintServiceClient.aidl | 37 | ||||
-rw-r--r-- | core/java/android/printservice/PrintJob.java | 254 | ||||
-rw-r--r-- | core/java/android/printservice/PrintService.java | 462 | ||||
-rw-r--r-- | core/java/android/printservice/PrintServiceInfo.aidl | 19 | ||||
-rw-r--r-- | core/java/android/printservice/PrintServiceInfo.java | 267 |
6 files changed, 1074 insertions, 0 deletions
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl new file mode 100644 index 0000000..eabd96d --- /dev/null +++ b/core/java/android/printservice/IPrintService.aidl @@ -0,0 +1,35 @@ +/* + * 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.printservice; + +import android.os.ICancellationSignal; +import android.print.PrintJobInfo; +import android.print.PrinterId; +import android.printservice.IPrintServiceClient; + +/** + * Top-level interface to a print service component. + * + * @hide + */ +oneway interface IPrintService { + void setClient(IPrintServiceClient client); + void requestCancelPrintJob(in PrintJobInfo printJob); + void onPrintJobQueued(in PrintJobInfo printJob); + void startPrinterDiscovery(); + void stopPrinterDiscovery(); +} diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl new file mode 100644 index 0000000..cff8c02 --- /dev/null +++ b/core/java/android/printservice/IPrintServiceClient.aidl @@ -0,0 +1,37 @@ +/* + * 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.printservice; + +import android.os.ParcelFileDescriptor; +import android.print.PrintJobInfo; +import android.print.PrinterId; +import android.print.PrinterInfo; + +/** + * The top-level interface from a print service to the system. + * + * @hide + */ +interface IPrintServiceClient { + List<PrintJobInfo> getPrintJobs(); + PrintJobInfo getPrintJob(int printJobId); + boolean setPrintJobState(int printJobId, int status); + boolean setPrintJobTag(int printJobId, String tag); + oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId); + oneway void addDiscoveredPrinters(in List<PrinterInfo> printers); + oneway void removeDiscoveredPrinters(in List<PrinterId> printers); +} diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java new file mode 100644 index 0000000..9688761 --- /dev/null +++ b/core/java/android/printservice/PrintJob.java @@ -0,0 +1,254 @@ +/* + * 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.printservice; + +import java.io.FileDescriptor; +import java.io.IOException; + +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.print.PrintJobInfo; +import android.util.Log; + +/** + * This class represents a print job from the perspective of a + * print service. It provides APIs for observing the print job + * state and performing operations on the print job. + */ +public final class PrintJob { + + private static final String LOG_TAG = "PrintJob"; + + private final int mId; + + private final IPrintServiceClient mPrintServiceClient; + + private PrintJobInfo mCachedInfo; + + PrintJob(PrintJobInfo info, IPrintServiceClient client) { + if (client == null) { + throw new IllegalStateException("Print serivice not connected!"); + } + mCachedInfo = info; + mId = info.getId(); + mPrintServiceClient = client; + } + + /** + * Gets the unique print job id. + * + * @return The id. + */ + public int getId() { + return mId; + } + + /** + * Gets the {@link PrintJobInfo} that describes this job. + * <p> + * <strong>Node:</strong>The returned info object is a snapshot of the + * current print job state. Every call to this method returns a fresh + * info object that reflects the current print jobs state. + * </p> + * + * @return The print job info. + */ + public PrintJobInfo getInfo() { + PrintJobInfo info = null; + try { + info = mPrintServiceClient.getPrintJob(mId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Couldn't get info for job: " + mId, re); + } + if (info != null) { + mCachedInfo = info; + } + return mCachedInfo; + } + + /** + * Gets whether this print job is queued. Such a print job is + * ready to be printed and can be started. + * + * @return Whether the print job is queued. + * + * @see #start() + */ + public boolean isQueued() { + return getInfo().getState() == PrintJobInfo.STATE_QUEUED; + } + + /** + * Gets whether this print job is started. Such a print job is + * being printed and can be completed or canceled or failed. + * + * @return Whether the print job is started. + * + * @see #complete() + * @see #cancel() + * @see #fail() + */ + public boolean isStarted() { + return getInfo().getState() == PrintJobInfo.STATE_STARTED; + } + + /** + * Starts the print job. You should call this method if {@link + * #isQueued()} returns true and you started printing. + * + * @return Whether the job as started. + * + * @see #isQueued() + */ + public boolean start() { + if (isQueued()) { + return setState(PrintJobInfo.STATE_STARTED); + } + return false; + } + + /** + * Completes the print job. You should call this method if {@link + * #isStarted()} returns true and you are done printing. + * + * @return Whether the job as completed. + * + * @see #isStarted() + */ + public boolean complete() { + if (isStarted()) { + return setState(PrintJobInfo.STATE_COMPLETED); + } + return false; + } + + /** + * Fails the print job. You should call this method if {@link + * #isStarted()} returns true you filed while printing. + * + * @return Whether the job as failed. + * + * @see #isStarted() + */ + public boolean fail(CharSequence error) { + // TODO: Propagate the error message to the UI. + if (isStarted()) { + return setState(PrintJobInfo.STATE_FAILED); + } + return false; + } + + /** + * Cancels the print job. You should call this method if {@link + * #isStarted()} returns true and you canceled the print job as a + * response to a call to {@link PrintService#onRequestCancelPrintJob( + * PrintJob)}. + * + * @return Whether the job as canceled. + * + * @see #isStarted() + */ + public boolean cancel() { + if (isStarted()) { + return setState(PrintJobInfo.STATE_CANCELED); + } + return false; + } + + /** + * Sets a tag that is valid in the context of a {@link PrintService} + * and is not interpreted by the system. For example, a print service + * may set as a tag the key of the print job returned by a remote + * print server, if the printing is off handed to a cloud based service. + * + * @param tag The tag. + * @return True if the tag was set, false otherwise. + */ + public boolean setTag(String tag) { + try { + return mPrintServiceClient.setPrintJobTag(mId, tag); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error setting tag for job:" + mId, re); + } + return false; + } + + /** + * Gets the data associated with this print job. It is a responsibility of + * the print service to open a stream to the returned file descriptor + * and fully read the content. + * + * @return A file descriptor for reading the data or <code>null</code>. + */ + public final FileDescriptor getData() { + ParcelFileDescriptor source = null; + ParcelFileDescriptor sink = null; + try { + ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); + source = fds[0]; + sink = fds[1]; + mPrintServiceClient.writePrintJobData(sink, mId); + return source.getFileDescriptor(); + } catch (IOException ioe) { + Log.e(LOG_TAG, "Error calling getting print job data!", ioe); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error calling getting print job data!", re); + } finally { + if (sink != null) { + try { + sink.close(); + } catch (IOException ioe) { + /* ignore */ + } + } + } + return null; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PrintJob other = (PrintJob) obj; + return (mId == other.mId); + } + + @Override + public int hashCode() { + return mId; + } + + private boolean setState(int state) { + // Best effort - update the state of the cached info since + // we may not be able to re-fetch it later if the job gets + // removed from the spooler. + mCachedInfo.setState(state); + try { + return mPrintServiceClient.setPrintJobState(mId, state); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error setting the state of job:" + mId, re); + } + return false; + } +} diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java new file mode 100644 index 0000000..d5cadc0 --- /dev/null +++ b/core/java/android/printservice/PrintService.java @@ -0,0 +1,462 @@ +/* + * 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.printservice; + +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.print.PrintJobInfo; +import android.print.PrinterId; +import android.print.PrinterInfo; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * <p> + * This is the base class for implementing print services. A print service + * knows how to discover and interact one or more printers via one or more + * protocols. + * </p> + * <h3>Printer discovery</h3> + * <p> + * A print service is responsible for discovering and reporting printers. + * A printer discovery period starts with a call to + * {@link #onStartPrinterDiscovery()} and ends with a call to + * {@link #onStopPrinterDiscovery()}. During a printer discovery + * period the print service reports newly discovered printers by + * calling {@link #addDiscoveredPrinters(List)} and added printers + * that disappeared by calling {@link #removeDiscoveredPrinters(List)}. + * Calls to {@link #addDiscoveredPrinters(List)} and + * {@link #removeDiscoveredPrinters(List)} before a call to + * {@link #onStartPrinterDiscovery()} and after a call to + * {@link #onStopPrinterDiscovery()} is a no-op. + * </p> + * <p> + * For every printer discovery period all printers have to be added. Each + * printer known to this print service should be added only once during a + * discovery period, unless it was added and then removed before that. + * Only an already added printer can be removed. + * </p> + * <h3>Print jobs</h3> + * <p> + * When a new print job targeted to the printers managed by this print + * service is queued, i.e. ready for processing by the print service, + * a call to {@link #onPrintJobQueued(PrintJob)} is made and the print + * service may handle it immediately or schedule that for an appropriate + * time in the future. The list of all print jobs for this service + * are be available by calling {@link #getPrintJobs()}. A queued print + * job is in a {@link PrintJobInfo#STATE_QUEUED} state. + * </p> + * <p> + * A print service is responsible for setting the print job state as + * appropriate while processing it. Initially, a print job is in a + * {@link PrintJobInfo#STATE_QUEUED} state which means that the data to + * be printed is spooled by the system and the print service can obtain + * that data by calling {@link PrintJob#getData()}. After the print + * service starts printing the data it should set the print job state + * to {@link PrintJobInfo#STATE_STARTED}. Upon successful completion, the + * print job state has to be set to {@link PrintJobInfo#STATE_COMPLETED}. + * In a case of a failure, the print job state should be set to + * {@link PrintJobInfo#STATE_FAILED}. If a print job is in a + * {@link PrintJobInfo#STATE_STARTED} state and the user requests to + * cancel it, the print service will receive a call to + * {@link #onRequestCancelPrintJob(PrintJob)} which requests from the + * service to do a best effort in canceling the job. In case the job + * is successfully canceled, its state has to be set to + * {@link PrintJobInfo#STATE_CANCELED}. + * </p> + * <h3>Lifecycle</h3> + * <p> + * The lifecycle of a print service is managed exclusively by the system + * and follows the established service lifecycle. Additionally, starting + * or stopping a print service is triggered exclusively by an explicit + * user action through enabling or disabling it in the device settings. + * After the system binds to a print service, it calls {@link #onConnected()}. + * This method can be overriden by clients to perform post binding setup. + * Also after the system unbinds from a print service, it calls + * {@link #onDisconnected()}. This method can be overriden by clients to + * perform post unbinding cleanup. + * </p> + * <h3>Declaration</h3> + * <p> + * A print service is declared as any other service in an AndroidManifest.xml + * but it must also specify that it handles the {@link android.content.Intent} + * with action {@link #SERVICE_INTERFACE}. Failure to declare this intent + * will cause the system to ignore the print service. Additionally, a print + * service must request the {@link android.Manifest.permission#BIND_PRINT_SERVICE} + * permission to ensure that only the system can bind to it. Failure to + * declare this intent will cause the system to ignore the print service. + * Following is an example declaration: + * </p> + * <pre> + * <service android:name=".MyPrintService" + * android:permission="android.permission.BIND_PRINT_SERVICE"> + * <intent-filter> + * <action android:name="android.printservice.PrintService" /> + * </intent-filter> + * . . . + * </service> + * </pre> + * <h3>Configuration</h3> + * <p> + * A print service can be configured by specifying an optional settings + * activity which exposes service specific options, an optional add + * prints activity which is used for manual addition of printers, etc. + * It is a responsibility of the system to launch the settings and add + * printers activities when appropriate. + * </p> + * <p> + * A print service is configured by providing a + * {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring + * the service. A service declaration with a meta-data tag is presented + * below: + * <pre> <service android:name=".MyPrintService" + * android:permission="android.permission.BIND_PRINT_SERVICE"> + * <intent-filter> + * <action android:name="android.printservice.PrintService" /> + * </intent-filter> + * <meta-data android:name="android.printservice" android:resource="@xml/printservice" /> + * </service></pre> + * </p> + * <p> + * For more details refer to {@link #SERVICE_META_DATA} and + * <code><{@link android.R.styleable#PrintService print-service}></code>. + * </p> + */ +public abstract class PrintService extends Service { + + private static final String LOG_TAG = PrintService.class.getSimpleName(); + + /** + * The {@link Intent} action that must be declared as handled by a service + * in its manifest to allow the system to recognize it as a print service. + */ + public static final String SERVICE_INTERFACE = "android.printservice.PrintService"; + + /** + * Name under which a PrintService component publishes additional information + * about itself. This meta-data must reference an XML resource containing a + * <code><{@link android.R.styleable#PrintService print-service}></code> + * tag. This is a a sample XML file configuring a print service: + * <pre> <print-service + * android:settingsActivity="foo.bar.MySettingsActivity" + * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity." + * . . . + * /></pre> + */ + public static final String SERVICE_META_DATA = "android.printservice"; + + private final Object mLock = new Object(); + + private Handler mHandler; + + private IPrintServiceClient mClient; + + private boolean mDiscoveringPrinters; + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + mHandler = new MyHandler(base.getMainLooper()); + } + + /** + * The system has connected to this service. + */ + protected void onConnected() { + /* do nothing */ + } + + /** + * The system has disconnected from this service. + */ + protected void onDisconnected() { + /* do nothing */ + } + + /** + * Callback requesting from this service to start printer discovery. + * At the end of the printer discovery period the system will call + * {@link #onStopPrinterDiscovery(). Discovered printers should be + * reported by calling #addDiscoveredPrinters(List) and reported ones + * that disappear should be reported by calling + * {@link #removeDiscoveredPrinters(List)}. + * + * @see #onStopPrinterDiscovery() + * @see #addDiscoveredPrinters(List) + * @see #removeDiscoveredPrinters(List) + */ + protected abstract void onStartPrinterDiscovery(); + + /** + * Callback requesting from this service to stop printer discovery. + * + * @see #onStartPrinterDiscovery() + * @see #addDiscoveredPrinters(List) + * @see #removeDiscoveredPrinters(List) + */ + protected abstract void onStopPrinterDiscovery(); + + /** + * Adds discovered printers. This method should be called during a + * printer discovery period, i.e. after a call to + * {@link #onStartPrinterDiscovery()} and before the corresponding + * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing. + * <p> + * <strong>Note:</strong> For every printer discovery period all + * printers have to be added. You can call this method as many times as + * necessary during the discovery period but should not pass in already + * added printers. If a printer is already added in the same printer + * discovery period, it will be ignored. + * </p> + * + * @param printers A list with discovered printers. + * + * @throws IllegalStateException If this service is not connected. + * + * @see #removeDiscoveredPrinters(List) + * @see #onStartPrinterDiscovery() + * @see #onStopPrinterDiscovery() + */ + public final void addDiscoveredPrinters(List<PrinterInfo> printers) { + synchronized (mLock) { + if (mClient == null) { + throw new IllegalStateException("Print serivice not connected!"); + } + if (mDiscoveringPrinters) { + try { + // Calling with a lock into the system is fine. + mClient.addDiscoveredPrinters(printers); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error adding discovered printers!", re); + } + } + } + } + + /** + * Removes discovered printers given their ids. This method should be called + * during a printer discovery period, i.e. after a call to + * {@link #onStartPrinterDiscovery()} and before the corresponding + * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing. + * <p> + * For every printer discovery period all printers have to be added. You + * should remove only printers that were added in this printer discovery + * period by a call to {@link #addDiscoveredPrinters(List)}. You can call + * this method as many times as necessary during the discovery period + * but should not pass in already removed printer ids. If a printer with + * a given id is already removed in the same discovery period, it will + * be ignored. + * </p> + * + * @param printerIds A list with disappeared printer ids. + * + * @throws IllegalStateException If this service is not connected. + * + * @see #addDiscoveredPrinters(List) + * @see #onStartPrinterDiscovery() + * @see #onStopPrinterDiscovery() + */ + public final void removeDiscoveredPrinters(List<PrinterId> printerIds) { + synchronized (mLock) { + if (mClient == null) { + throw new IllegalStateException("Print serivice not connected!"); + } + if (mDiscoveringPrinters) { + try { + // Calling with a lock into the system is fine. + mClient.removeDiscoveredPrinters(printerIds); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error removing discovered printers!", re); + } + } + } + } + + /** + * Called when canceling of a print job is requested. The service + * should do best effort to fulfill the request. After the print + * job is canceled it state has to be set to + * {@link PrintJobInfo#STATE_CANCELED}. + * + * @param printJob The print job to be canceled. + */ + protected void onRequestCancelPrintJob(PrintJob printJob) { + } + + /** + * Called when there is a queued print job for one of the printers + * managed by this print service. A queued print job is ready for + * processing by a print service which can get the data to be printed + * by calling {@link PrintJob#getData()}. This service may start + * processing the passed in print job or schedule handling of queued + * print jobs at a convenient time. The service can get the print + * jobs by a call to {@link #getPrintJobs()} and examine their state + * to find the ones with state {@link PrintJobInfo#STATE_QUEUED}. + * + * @param printJob The new queued print job. + * + * @see #getPrintJobs() + */ + protected abstract void onPrintJobQueued(PrintJob printJob); + + /** + * Gets the print jobs for the printers managed by this service. + * + * @return The print jobs. + * + * @throws IllegalStateException If this service is not connected. + */ + public final List<PrintJob> getPrintJobs() { + synchronized (mLock) { + if (mClient == null) { + throw new IllegalStateException("Print serivice not connected!"); + } + try { + List<PrintJob> printJobs = null; + List<PrintJobInfo> printJobInfos = mClient.getPrintJobs(); + if (printJobInfos != null) { + final int printJobInfoCount = printJobInfos.size(); + printJobs = new ArrayList<PrintJob>(printJobInfoCount); + for (int i = 0; i < printJobInfoCount; i++) { + printJobs.add(new PrintJob(printJobInfos.get(i), mClient)); + } + } + if (printJobs != null) { + return printJobs; + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error calling getPrintJobs()", re); + } + return Collections.emptyList(); + } + } + + /** + * Generates a global printer id from a local id. The local id is unique + * only within this print service. + * + * @param localId The local id. + * @return Global printer id. + */ + public final PrinterId generatePrinterId(String localId) { + return new PrinterId(new ComponentName(getPackageName(), + getClass().getName()), localId); + } + + @Override + public final IBinder onBind(Intent intent) { + return new IPrintService.Stub() { + @Override + public void setClient(IPrintServiceClient client) { + mHandler.obtainMessage(MyHandler.MESSAGE_SET_CLEINT, client).sendToTarget(); + } + + @Override + public void startPrinterDiscovery() { + mHandler.sendEmptyMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY); + } + + @Override + public void stopPrinterDiscovery() { + mHandler.sendEmptyMessage(MyHandler.MESSAGE_STOP_PRINTER_DISCOVERY); + } + + @Override + public void requestCancelPrintJob(PrintJobInfo printJob) { + mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB, printJob).sendToTarget(); + } + + @Override + public void onPrintJobQueued(PrintJobInfo printJob) { + mHandler.obtainMessage(MyHandler.MESSAGE_ON_PRINTJOB_QUEUED, + printJob).sendToTarget(); + } + }; + } + + private final class MyHandler extends Handler { + public static final int MESSAGE_START_PRINTER_DISCOVERY = 1; + public static final int MESSAGE_STOP_PRINTER_DISCOVERY = 2; + public static final int MESSAGE_CANCEL_PRINTJOB = 3; + public static final int MESSAGE_ON_PRINTJOB_QUEUED = 4; + public static final int MESSAGE_SET_CLEINT = 5; + + public MyHandler(Looper looper) { + super(looper, null, true); + } + + @Override + public void handleMessage(Message message) { + final int action = message.what; + switch (action) { + case MESSAGE_START_PRINTER_DISCOVERY: { + synchronized (mLock) { + mDiscoveringPrinters = true; + } + onStartPrinterDiscovery(); + } break; + + case MESSAGE_STOP_PRINTER_DISCOVERY: { + synchronized (mLock) { + mDiscoveringPrinters = false; + } + onStopPrinterDiscovery(); + } break; + + case MESSAGE_CANCEL_PRINTJOB: { + PrintJobInfo printJob = (PrintJobInfo) message.obj; + onRequestCancelPrintJob(new PrintJob(printJob, mClient)); + } break; + + case MESSAGE_ON_PRINTJOB_QUEUED: { + PrintJobInfo printJob = (PrintJobInfo) message.obj; + onPrintJobQueued(new PrintJob(printJob, mClient)); + } break; + + case MESSAGE_SET_CLEINT: { + IPrintServiceClient client = (IPrintServiceClient) message.obj; + synchronized (mLock) { + mClient = client; + if (client == null) { + mDiscoveringPrinters = false; + } + } + if (client != null) { + onConnected(); + } else { + onStopPrinterDiscovery(); + onDisconnected(); + } + } break; + + default: { + throw new IllegalArgumentException("Unknown message: " + action); + } + } + } + } +} diff --git a/core/java/android/printservice/PrintServiceInfo.aidl b/core/java/android/printservice/PrintServiceInfo.aidl new file mode 100644 index 0000000..95b67f2 --- /dev/null +++ b/core/java/android/printservice/PrintServiceInfo.aidl @@ -0,0 +1,19 @@ +/** + * 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.printservice; + +parcelable PrintServiceInfo; diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java new file mode 100644 index 0000000..0370a25 --- /dev/null +++ b/core/java/android/printservice/PrintServiceInfo.java @@ -0,0 +1,267 @@ +/* + * 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.printservice; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * This class describes a {@link PrintService}. A print service knows + * how to communicate with one or more printers over one or more protocols + * and exposes printers for use by the applications via the platform print + * APIs. + * + * @see PrintService + * @see android.print.PrintManager + * + * @hide + */ +public final class PrintServiceInfo implements Parcelable { + + private static final boolean DEBUG = false; + + private static final String LOG_TAG = PrintServiceInfo.class.getSimpleName(); + + private static final String TAG_PRINT_SERVICE = "print-service"; + + private final String mId; + + private final ResolveInfo mResolveInfo; + + private final String mSettingsActivityName; + + private final String mAddPrintersActivityName; + + /** + * Creates a new instance. + * + * @hide + */ + public PrintServiceInfo(Parcel parcel) { + mId = parcel.readString(); + mResolveInfo = parcel.readParcelable(null); + mSettingsActivityName = parcel.readString(); + mAddPrintersActivityName = parcel.readString(); + } + + /** + * Creates a new instance. + * + * @param resolveInfo The service resolve info. + * @param settingsActivityName Optional settings activity name. + * @param addPrintersActivityName Optional add printers activity name. + */ + public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName, + String addPrintersActivityName) { + mId = new ComponentName(resolveInfo.serviceInfo.packageName, + resolveInfo.serviceInfo.name).flattenToString(); + mResolveInfo = resolveInfo; + mSettingsActivityName = settingsActivityName; + mAddPrintersActivityName = addPrintersActivityName; + } + + /** + * Creates a new instance. + * + * @param resolveInfo The service resolve info. + * @param context Context for accessing resources. + * @throws XmlPullParserException If a XML parsing error occurs. + * @throws IOException If a I/O error occurs. + * @hide + */ + public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) { + String settingsActivityName = null; + String addPrintersActivityName = null; + + XmlResourceParser parser = null; + PackageManager packageManager = context.getPackageManager(); + parser = resolveInfo.serviceInfo.loadXmlMetaData(packageManager, + PrintService.SERVICE_META_DATA); + if (parser != null) { + try { + int type = 0; + while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { + type = parser.next(); + } + + String nodeName = parser.getName(); + if (!TAG_PRINT_SERVICE.equals(nodeName)) { + throw new XmlPullParserException( + "Meta-data does not start with" + TAG_PRINT_SERVICE + " tag"); + } + + Resources resources = packageManager.getResourcesForApplication( + resolveInfo.serviceInfo.applicationInfo); + AttributeSet allAttributes = Xml.asAttributeSet(parser); + TypedArray attributes = resources.obtainAttributes(allAttributes, + com.android.internal.R.styleable.PrintService); + + settingsActivityName = attributes.getString( + com.android.internal.R.styleable.PrintService_settingsActivity); + + addPrintersActivityName = attributes.getString( + com.android.internal.R.styleable.PrintService_addPrintersActivity); + + attributes.recycle(); + } catch (IOException ioe) { + Log.w(LOG_TAG, "Error reading meta-data:" + ioe); + } catch (XmlPullParserException xppe) { + Log.w(LOG_TAG, "Error reading meta-data:" + xppe); + } catch (NameNotFoundException e) { + Log.e(LOG_TAG, "Unable to load resources for: " + + resolveInfo.serviceInfo.packageName); + } finally { + if (parser != null) { + parser.close(); + } + } + } + + return new PrintServiceInfo(resolveInfo, settingsActivityName, addPrintersActivityName); + } + + /** + * The accessibility service id. + * <p> + * <strong>Generated by the system.</strong> + * </p> + * + * @return The id. + */ + public String getId() { + return mId; + } + + /** + * The service {@link ResolveInfo}. + * + * @return The info. + */ + public ResolveInfo getResolveInfo() { + return mResolveInfo; + } + + /** + * The settings activity name. + * <p> + * <strong>Statically set from + * {@link PrintService#SERVICE_META_DATA meta-data}.</strong> + * </p> + * + * @return The settings activity name. + */ + public String getSettingsActivityName() { + return mSettingsActivityName; + } + + /** + * The add printers activity name. + * <p> + * <strong>Statically set from + * {@link PrintService#SERVICE_META_DATA meta-data}.</strong> + * </p> + * + * @return The add printers activity name. + */ + public String getAddPrintersActivityName() { + return mAddPrintersActivityName; + } + + /** + * {@inheritDoc} + */ + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flagz) { + parcel.writeString(mId); + parcel.writeParcelable(mResolveInfo, 0); + parcel.writeString(mSettingsActivityName); + parcel.writeString(mAddPrintersActivityName); + } + + @Override + public int hashCode() { + return 31 * 1 + ((mId == null) ? 0 : mId.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PrintServiceInfo other = (PrintServiceInfo) obj; + if (mId == null) { + if (other.mId != null) { + return false; + } + } else if (!mId.equals(other.mId)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("PrintServiceInfo{"); + builder.append("id:").append(mId).append(", "); + builder.append("resolveInfo:").append(mResolveInfo).append(", "); + if (DEBUG) { + builder.append("settingsActivityName:").append(mSettingsActivityName); + builder.append("addPrintersActivityName:").append(mAddPrintersActivityName); + } else if (mSettingsActivityName != null || mAddPrintersActivityName != null) { + builder.append("<has meta-data>"); + } + builder.append("}"); + return builder.toString(); + } + + public static final Parcelable.Creator<PrintServiceInfo> CREATOR = + new Parcelable.Creator<PrintServiceInfo>() { + public PrintServiceInfo createFromParcel(Parcel parcel) { + return new PrintServiceInfo(parcel); + } + + public PrintServiceInfo[] newArray(int size) { + return new PrintServiceInfo[size]; + } + }; +} |