diff options
author | Svetoslav Ganov <svetoslavganov@google.com> | 2013-08-02 21:24:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-08-02 21:24:38 +0000 |
commit | bb9b30078739dba99ffa094b35fde0118c245097 (patch) | |
tree | f03f4fa6218f39ca80108ab294dc7633c55614e9 | |
parent | 4d79d7821d45719b8efe71e68617e511084cb8a1 (diff) | |
parent | 8c43376ea83a67414bd6823a472b76d41160239e (diff) | |
download | frameworks_base-bb9b30078739dba99ffa094b35fde0118c245097.zip frameworks_base-bb9b30078739dba99ffa094b35fde0118c245097.tar.gz frameworks_base-bb9b30078739dba99ffa094b35fde0118c245097.tar.bz2 |
Merge " First cut of the print notifications."
20 files changed, 574 insertions, 294 deletions
diff --git a/api/current.txt b/api/current.txt index 402c2dc..18c6938 100644 --- a/api/current.txt +++ b/api/current.txt @@ -18731,7 +18731,7 @@ package android.print { public final class PrinterId implements android.os.Parcelable { method public int describeContents(); - method public java.lang.String getLocalId(); + method public java.lang.String getPrinterName(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -18744,7 +18744,6 @@ package android.print { method public int getFittingModes(); method public android.print.PrinterId getId(); method public java.util.List<android.print.PrintAttributes.Tray> getInputTrays(); - method public java.lang.CharSequence getLabel(); method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes(); method public android.print.PrintAttributes.Margins getMinMargins(); method public int getOrientations(); @@ -18758,7 +18757,7 @@ package android.print { } public static final class PrinterInfo.Builder { - ctor public PrinterInfo.Builder(android.print.PrinterId, java.lang.CharSequence); + ctor public PrinterInfo.Builder(android.print.PrinterId); method public android.print.PrinterInfo.Builder addInputTray(android.print.PrintAttributes.Tray, boolean); method public android.print.PrinterInfo.Builder addMediaSize(android.print.PrintAttributes.MediaSize, boolean); method public android.print.PrinterInfo.Builder addOutputTray(android.print.PrintAttributes.Tray, boolean); diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index 37ae2ca..7155096 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -33,4 +33,5 @@ interface IPrintManager { in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes, int appId, int userId); void cancelPrintJob(int printJobId, int appId, int userId); + void restartPrintJob(int printJobId, int appId, int userId); } diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl index c55205d..428f972 100644 --- a/core/java/android/print/IPrintSpooler.aidl +++ b/core/java/android/print/IPrintSpooler.aidl @@ -40,10 +40,8 @@ oneway interface IPrintSpooler { void createPrintJob(String printJobName, in IPrintClient client, in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes, IPrintSpoolerCallbacks callback, int appId, int sequence); - void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback, - int appId, int sequence); - void setPrintJobState(int printJobId, int status, IPrintSpoolerCallbacks callback, - int sequence); + void setPrintJobState(int printJobId, int status, CharSequence error, + IPrintSpoolerCallbacks callback, int sequence); void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback, int sequence); void writePrintJobData(in ParcelFileDescriptor fd, int printJobId); diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 39546f3..096dcd5 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -124,6 +124,9 @@ public final class PrintJobInfo implements Parcelable { /** How many copies to print. */ private int mCopies; + /** Failure reason if this job failed. */ + private CharSequence mFailureReason; + /** The pages to print */ private PageRange[] mPageRanges; @@ -148,6 +151,7 @@ public final class PrintJobInfo implements Parcelable { mUserId = other.mUserId; mTag = other.mTag; mCopies = other.mCopies; + mFailureReason = other.mFailureReason; mPageRanges = other.mPageRanges; mAttributes = other.mAttributes; mDocumentInfo = other.mDocumentInfo; @@ -163,6 +167,9 @@ public final class PrintJobInfo implements Parcelable { mTag = parcel.readString(); mCopies = parcel.readInt(); if (parcel.readInt() == 1) { + mFailureReason = parcel.readCharSequence(); + } + if (parcel.readInt() == 1) { Parcelable[] parcelables = parcel.readParcelableArray(null); mPageRanges = new PageRange[parcelables.length]; for (int i = 0; i < parcelables.length; i++) { @@ -345,6 +352,28 @@ public final class PrintJobInfo implements Parcelable { } /** + * The failure reason if this print job failed. + * + * @return The failure reason. + * + * @hide + */ + public CharSequence getFailureReason() { + return mFailureReason; + } + + /** + * The failure reason if this print job failed. + * + * @param failureReason The failure reason. + * + * @hide + */ + public void setFailureReason(CharSequence failureReason) { + mFailureReason = failureReason; + } + + /** * Gets the included pages. * * @return The included pages or <code>null</code> if not set. @@ -421,6 +450,12 @@ public final class PrintJobInfo implements Parcelable { parcel.writeInt(mUserId); parcel.writeString(mTag); parcel.writeInt(mCopies); + if (mFailureReason != null) { + parcel.writeInt(1); + parcel.writeCharSequence(mFailureReason); + } else { + parcel.writeInt(0); + } if (mPageRanges != null) { parcel.writeInt(1); parcel.writeParcelableArray(mPageRanges, flags); diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java index e27fbb2..8462736 100644 --- a/core/java/android/print/PrinterId.java +++ b/core/java/android/print/PrinterId.java @@ -26,26 +26,26 @@ import android.text.TextUtils; */ public final class PrinterId implements Parcelable { - private final ComponentName mServiceComponentName; + private final ComponentName mServiceName; - private final String mLocalId; + private final String mPrinterName; /** * Creates a new instance. * - * @param serviceComponentName The managing print service. - * @param localId The unique id within the managing service. + * @param serviceName The managing print service. + * @param printerName The unique name within the managing service. * * @hide */ - public PrinterId(ComponentName serviceComponentName, String localId) { - mServiceComponentName = serviceComponentName; - mLocalId = localId; + public PrinterId(ComponentName serviceName, String printerName) { + mServiceName = serviceName; + mPrinterName = printerName; } private PrinterId(Parcel parcel) { - mServiceComponentName = parcel.readParcelable(null); - mLocalId = parcel.readString(); + mServiceName = parcel.readParcelable(null); + mPrinterName = parcel.readString(); } /** @@ -55,18 +55,18 @@ public final class PrinterId implements Parcelable { * * @hide */ - public ComponentName getService() { - return mServiceComponentName; + public ComponentName getServiceName() { + return mServiceName; } /** - * Gets the local id of this printer in the context + * Gets the name of this printer which is unique in the context * of the print service that manages it. * - * @return The local id. + * @return The printer name. */ - public String getLocalId() { - return mLocalId; + public String getPrinterName() { + return mPrinterName; } @Override @@ -76,8 +76,8 @@ public final class PrinterId implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeParcelable(mServiceComponentName, flags); - parcel.writeString(mLocalId); + parcel.writeParcelable(mServiceName, flags); + parcel.writeString(mPrinterName); } @Override @@ -92,14 +92,14 @@ public final class PrinterId implements Parcelable { return false; } PrinterId other = (PrinterId) object; - if (mServiceComponentName == null) { - if (other.mServiceComponentName != null) { + if (mServiceName == null) { + if (other.mServiceName != null) { return false; } - } else if (!mServiceComponentName.equals(other.mServiceComponentName)) { + } else if (!mServiceName.equals(other.mServiceName)) { return false; } - if (!TextUtils.equals(mLocalId, other.mLocalId)) { + if (!TextUtils.equals(mPrinterName, other.mPrinterName)) { return false; } return true; @@ -109,9 +109,9 @@ public final class PrinterId implements Parcelable { public int hashCode() { final int prime = 31; int hashCode = 1; - hashCode = prime * hashCode + ((mServiceComponentName != null) - ? mServiceComponentName.hashCode() : 1); - hashCode = prime * hashCode + mLocalId.hashCode(); + hashCode = prime * hashCode + ((mServiceName != null) + ? mServiceName.hashCode() : 1); + hashCode = prime * hashCode + mPrinterName.hashCode(); return hashCode; } @@ -119,9 +119,9 @@ public final class PrinterId implements Parcelable { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("PrinterId{"); - builder.append(mServiceComponentName.flattenToString()); + builder.append(mServiceName.flattenToString()); builder.append(":"); - builder.append(mLocalId); + builder.append(mPrinterName); builder.append('}'); return builder.toString(); } diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index 15bcb73..10cecca 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -22,7 +22,6 @@ import android.print.PrintAttributes.Margins; import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Resolution; import android.print.PrintAttributes.Tray; -import android.text.TextUtils; import java.util.ArrayList; import java.util.Arrays; @@ -59,7 +58,6 @@ public final class PrinterInfo implements Parcelable { // TODO: Add printer status constants. private PrinterId mId; - private CharSequence mLabel; private int mStatus; private Margins mMinMargins = DEFAULT_MARGINS; @@ -92,7 +90,6 @@ public final class PrinterInfo implements Parcelable { */ public void copyFrom(PrinterInfo other) { mId = other.mId; - mLabel = other.mLabel; mStatus = other.mStatus; mMinMargins = other.mMinMargins; @@ -162,15 +159,6 @@ public final class PrinterInfo implements Parcelable { } /** - * Gets the human readable printer label. - * - * @return The human readable label. - */ - public CharSequence getLabel() { - return mLabel; - } - - /** * Gets the status of the printer. * * @return The status. @@ -343,7 +331,6 @@ public final class PrinterInfo implements Parcelable { private PrinterInfo(Parcel parcel) { mId = parcel.readParcelable(null); - mLabel = parcel.readCharSequence(); mStatus = parcel.readInt(); mMinMargins = readMargins(parcel); @@ -369,7 +356,6 @@ public final class PrinterInfo implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeParcelable(mId, flags); - parcel.writeCharSequence(mLabel); parcel.writeInt(mStatus); writeMargins(mMinMargins, parcel); @@ -392,7 +378,6 @@ public final class PrinterInfo implements Parcelable { final int prime = 31; int result = 1; result = prime * result + ((mId == null) ? 0 : mId.hashCode()); - result = prime * result + ((mLabel == null) ? 0 : mLabel.hashCode()); result = prime * result + mStatus; result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode()); result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode()); @@ -427,9 +412,6 @@ public final class PrinterInfo implements Parcelable { } else if (!mId.equals(other.mId)) { return false; } - if (!TextUtils.equals(mLabel, other.mLabel)) { - return false; - } if (mStatus != other.mStatus) { return false; } @@ -498,7 +480,6 @@ public final class PrinterInfo implements Parcelable { StringBuilder builder = new StringBuilder(); builder.append("PrinterInfo{"); builder.append(mId).append(", \""); - builder.append(mLabel); builder.append("\"}"); return builder.toString(); } @@ -639,20 +620,14 @@ public final class PrinterInfo implements Parcelable { * Creates a new instance. * * @param printerId The printer id. Cannot be null. - * @param label The human readable printer label. Cannot be null or empty. * * @throws IllegalArgumentException If the printer id is null. - * @throws IllegalArgumentException If the label is empty. */ - public Builder(PrinterId printerId, CharSequence label) { + public Builder(PrinterId printerId) { if (printerId == null) { throw new IllegalArgumentException("printerId cannot be null."); } - if (TextUtils.isEmpty(label)) { - throw new IllegalArgumentException("label cannot be empty."); - } mPrototype = new PrinterInfo(); - mPrototype.mLabel = label; mPrototype.mId = printerId; } diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl index cdde4d8..f00b37c4 100644 --- a/core/java/android/printservice/IPrintServiceClient.aidl +++ b/core/java/android/printservice/IPrintServiceClient.aidl @@ -29,7 +29,7 @@ import android.print.PrinterInfo; interface IPrintServiceClient { List<PrintJobInfo> getPrintJobInfos(); PrintJobInfo getPrintJobInfo(int printJobId); - boolean setPrintJobState(int printJobId, int status); + boolean setPrintJobState(int printJobId, int state, CharSequence error); boolean setPrintJobTag(int printJobId, String tag); oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId); } diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index 0ac5a13..64c079e 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -121,7 +121,7 @@ public final class PrintJob { */ public boolean start() { if (isQueued()) { - return setState(PrintJobInfo.STATE_STARTED); + return setState(PrintJobInfo.STATE_STARTED, null); } return false; } @@ -136,41 +136,43 @@ public final class PrintJob { */ public boolean complete() { if (isStarted()) { - return setState(PrintJobInfo.STATE_COMPLETED); + return setState(PrintJobInfo.STATE_COMPLETED, null); } return false; } /** * Fails the print job. You should call this method if {@link - * #isStarted()} returns true you filed while printing. + * #isQueued()} or {@link #isStarted()} returns true you failed + * while printing. * - * @param error The reason for the failure. + * @param error The human readable, short, and translated reason + * for the failure. * @return Whether the job was failed. * + * @see #isQueued() * @see #isStarted() */ public boolean fail(CharSequence error) { - // TODO: Propagate the error message to the UI. - if (isStarted()) { - return setState(PrintJobInfo.STATE_FAILED); + if (isQueued() || isStarted()) { + return setState(PrintJobInfo.STATE_FAILED, error); } 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)}. + * #isQueued()} or {@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); + if (isQueued() || isStarted()) { + return setState(PrintJobInfo.STATE_CANCELED, null); } return false; } @@ -222,13 +224,14 @@ public final class PrintJob { || state == PrintJobInfo.STATE_CANCELED; } - private boolean setState(int state) { + private boolean setState(int state, CharSequence error) { try { - if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state)) { + if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) { // 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 as a result of the state change. mCachedInfo.setState(state); + mCachedInfo.setFailureReason(error); return true; } } catch (RemoteException re) { diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 15e1b73..49384db 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -428,15 +428,14 @@ public abstract class PrintService extends Service { } /** - * Generates a global printer id from a local id. The local id is unique - * only within this print service. + * Generates a global printer id given the printer's locally unique name. * - * @param localId The local id. + * @param printerName The printer name. * @return Global printer id. */ - public final PrinterId generatePrinterId(String localId) { + public final PrinterId generatePrinterId(String printerName) { return new PrinterId(new ComponentName(getPackageName(), - getClass().getName()), localId); + getClass().getName()), printerName); } @Override diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java index 43dd1b6..128628d 100644 --- a/core/java/android/printservice/PrintServiceInfo.java +++ b/core/java/android/printservice/PrintServiceInfo.java @@ -113,23 +113,23 @@ public final class PrintServiceInfo implements Parcelable { String nodeName = parser.getName(); if (!TAG_PRINT_SERVICE.equals(nodeName)) { - throw new XmlPullParserException( - "Meta-data does not start with " + TAG_PRINT_SERVICE + " tag"); + Log.e(LOG_TAG, "Ignoring meta-data that does not start with " + + TAG_PRINT_SERVICE + " tag"); + } else { + 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(); } - - 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) { diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml index f0bbfa4..72d064a 100644 --- a/packages/PrintSpooler/AndroidManifest.xml +++ b/packages/PrintSpooler/AndroidManifest.xml @@ -25,8 +25,8 @@ <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17"/> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + <uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/> + <uses-permission android:name="android.permission.WAKE_LOCK"/> <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE" android:label="@string/permlab_bindPrintSpoolerService" @@ -50,6 +50,11 @@ android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar"> </activity> + <receiver + android:name=".NotificationController$NotificationBroadcastReceiver" + android:exported="false" > + </receiver> + </application> </manifest> diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index 1762693..f400f21 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -55,6 +55,28 @@ <!-- Title if the number of pages in a printed document is unknown. [CHAR LIMIT=20] --> <string name="page_count_unknown">unknown</string> + <!-- Notifications --> + + <!-- Template for the notificaiton label for a queued print job. [CHAR LIMIT=25] --> + <string name="queued_notification_title_template">Queued <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string> + + <!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] --> + <string name="printing_notification_title_template">Printing <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string> + + <!-- Template for the notificaiton label for a cancelling print job. [CHAR LIMIT=25] --> + <string name="cancelling_notification_title_template">Cancelling <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string> + + <!-- Template for the notificaiton label for a failed print job. [CHAR LIMIT=25] --> + <string name="failed_notification_title_template">Printer error <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string> + + <!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] --> + <string name="cancel">Cancel</string> + + <!-- Label for the notification button for restrating a filed print job. [CHAR LIMIT=25] --> + <string name="restart">Restart</string> + + <!-- Arrays --> + <!-- Color mode labels. --> <string-array name="color_mode_labels"> <!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] --> diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java new file mode 100644 index 0000000..e4de4b8 --- /dev/null +++ b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java @@ -0,0 +1,245 @@ +/* + * 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 com.android.printspooler; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.print.IPrintManager; +import android.print.PrintJobInfo; +import android.print.PrintManager; +import android.util.Log; + +/** + * This class is responsible for updating the print notifications + * based on print job state transitions. + */ +public class NotificationController { + public static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + + public static final String LOG_TAG = "NotificationController"; + + private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB"; + private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB"; + private static final String INTENT_EXTRA_PRINTJOB_ID = "INTENT_EXTRA_PRINTJOB_ID"; + + private final Context mContext; + private final NotificationManager mNotificationManager; + + public NotificationController(Context context) { + mContext = context; + mNotificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } + + public void onPrintJobStateChanged(PrintJobInfo printJob, int oldState) { + if (DEBUG) { + Log.i(LOG_TAG, "onPrintJobStateChanged() printJobId: " + printJob.getId() + + " oldState: " + PrintJobInfo.stateToString(oldState) + + " newState:" + PrintJobInfo.stateToString(printJob.getState())); + } + switch (printJob.getState()) { + case PrintJobInfo.STATE_QUEUED: { + createQueuingNotificaiton(printJob); + } break; + + case PrintJobInfo.STATE_STARTED: { + createPrintingNotificaiton(printJob); + } break; + + case PrintJobInfo.STATE_FAILED: { + createFailedNotificaiton(printJob); + } break; + + case PrintJobInfo.STATE_COMPLETED: + case PrintJobInfo.STATE_CANCELED: { + removeNotification(printJob.getId()); + } break; + } + } + + private void createQueuingNotificaiton(PrintJobInfo printJob) { + Notification.Builder builder = new Notification.Builder(mContext) + // TODO: Use appropriate icon when assets are ready + .setSmallIcon(android.R.drawable.ic_secure) + .setContentTitle(mContext.getString(R.string.queued_notification_title_template, + printJob.getLabel())) + // TODO: Use appropriate icon when assets are ready + .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel), + createCancelIntent(printJob.getId())) + .setContentText(printJob.getPrinterId().getPrinterName()) + .setOngoing(true) + .setWhen(System.currentTimeMillis()) + .setShowWhen(true); + mNotificationManager.notify(printJob.getId(), builder.build()); + } + + private void createPrintingNotificaiton(PrintJobInfo printJob) { + Notification.Builder builder = new Notification.Builder(mContext) + // TODO: Use appropriate icon when assets are ready + .setSmallIcon(android.R.drawable.ic_secure) + .setContentTitle(mContext.getString(R.string.printing_notification_title_template, + printJob.getLabel())) + // TODO: Use appropriate icon when assets are ready + .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel), + createCancelIntent(printJob.getId())) + .setContentText(printJob.getPrinterId().getPrinterName()) + .setOngoing(true) + .setWhen(System.currentTimeMillis()) + .setShowWhen(true); + mNotificationManager.notify(printJob.getId(), builder.build()); + } + + private void createFailedNotificaiton(PrintJobInfo printJob) { + Notification.Builder builder = new Notification.Builder(mContext) + // TODO: Use appropriate icon when assets are ready + .setSmallIcon(android.R.drawable.ic_secure) + .setContentTitle(mContext.getString(R.string.failed_notification_title_template, + printJob.getLabel())) + // TODO: Use appropriate icon when assets are ready + .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel), + createCancelIntent(printJob.getId())) + // TODO: Use appropriate icon when assets are ready + .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart), + createRestartIntent(printJob.getId())) + .setContentText(printJob.getFailureReason()) + .setOngoing(true) + .setWhen(System.currentTimeMillis()) + .setShowWhen(true); + mNotificationManager.notify(printJob.getId(), builder.build()); + } + + private void removeNotification(int printJobId) { + mNotificationManager.cancel(printJobId); + } + + private PendingIntent createCancelIntent(int printJobId) { + Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class); + intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + String.valueOf(printJobId)); + intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId); + return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT); + } + + private PendingIntent createRestartIntent(int printJobId) { + Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class); + intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + String.valueOf(printJobId)); + intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId); + return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT); + } + + public static final class NotificationBroadcastReceiver extends BroadcastReceiver { + private static final String LOG_TAG = "NotificationBroadcastReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) { + final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID); + handleCancelPrintJob(context, printJobId); + } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) { + final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID); + handleRestartPrintJob(context, printJobId); + } + } + + private void handleCancelPrintJob(final Context context, final int printJobId) { + if (DEBUG) { + Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId); + } + + PrintSpooler printSpooler = PrintSpooler.getInstance(context); + + final PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId, + PrintManager.APP_ID_ANY); + + if (printJob == null || printJob.getState() == PrintJobInfo.STATE_CANCELED) { + return; + } + + // Put up a notification that we are trying to cancel. + NotificationManager notificationManager = (NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + + Notification.Builder builder = new Notification.Builder(context) + // TODO: Use appropriate icon when assets are ready + .setSmallIcon(android.R.drawable.ic_secure) + .setContentTitle(context.getString( + R.string.cancelling_notification_title_template, + printJob.getLabel())) + .setContentText(printJob.getPrinterId().getPrinterName()) + .setOngoing(true) + .setWhen(System.currentTimeMillis()) + .setShowWhen(true); + + notificationManager.notify(printJob.getId(), builder.build()); + + // We need to request the cancellation to be done by the print + // manager service since it has to communicate with the managing + // print service to request the cancellation. Also we need the + // system service to be bound to the spooler since canceling a + // print job will trigger persistence of current jobs which is + // done on another thread and until it finishes the spooler has + // to be kept around. + IPrintManager printManager = IPrintManager.Stub.asInterface( + ServiceManager.getService(Context.PRINT_SERVICE)); + + try { + printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY, + UserHandle.myUserId()); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error requestion print job cancellation", re); + } + } + + private void handleRestartPrintJob(final Context context, final int printJobId) { + if (DEBUG) { + Log.i(LOG_TAG, "handleRestartPrintJob() printJobId:" + printJobId); + } + + PrintSpooler printSpooler = PrintSpooler.getInstance(context); + + PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId, + PrintManager.APP_ID_ANY); + + if (printJob == null || printJob.getState() != PrintJobInfo.STATE_FAILED) { + return; + } + + // We need to request the restart to be done by the print manager + // service since the latter must be bound to the spooler because + // restarting a print job will trigger persistence of current jobs + // which is done on another thread and until it finishes the spooler has + // to be kept around. + IPrintManager printManager = IPrintManager.Stub.asInterface( + ServiceManager.getService(Context.PRINT_SERVICE)); + + try { + printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY, + UserHandle.myUserId()); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error requestion print job restart", re); + } + } + } +} diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index d61fd2c..484c8a9 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -50,7 +50,6 @@ import android.text.TextUtils.SimpleStringSplitter; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; -import android.view.Choreographer; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -192,16 +191,16 @@ public class PrintJobConfigActivity extends Activity { mPrinterDiscoveryObserver = null; if (mController.isCancelled() || mController.isFailed()) { mSpooler.setPrintJobState(mPrintJobId, - PrintJobInfo.STATE_CANCELED); + PrintJobInfo.STATE_CANCELED, null); } else if (mController.hasStarted()) { mController.finish(); if (mEditor.isPrintConfirmed()) { if (mController.isFinished()) { mSpooler.setPrintJobState(mPrintJobId, - PrintJobInfo.STATE_QUEUED); + PrintJobInfo.STATE_QUEUED, null); } else { mSpooler.setPrintJobState(mPrintJobId, - PrintJobInfo.STATE_CANCELED); + PrintJobInfo.STATE_CANCELED, null); } } } @@ -568,6 +567,10 @@ public class PrintJobConfigActivity extends Activity { @Override public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) { if (spinner == mDestinationSpinner) { + if (mIgnoreNextDestinationChange) { + mIgnoreNextDestinationChange = false; + return; + } mCurrPrintAttributes.clear(); SpinnerItem<PrinterInfo> dstItem = mDestinationSpinnerAdapter.getItem(position); if (dstItem != null) { @@ -590,12 +593,20 @@ public class PrintJobConfigActivity extends Activity { } updateUi(); } else if (spinner == mMediaSizeSpinner) { + if (mIgnoreNextMediaSizeChange) { + mIgnoreNextMediaSizeChange = false; + return; + } SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position); mCurrPrintAttributes.setMediaSize(mediaItem.value); if (!hasErrors()) { mController.update(); } } else if (spinner == mColorModeSpinner) { + if (mIgnoreNextColorModeChange) { + mIgnoreNextColorModeChange = false; + return; + } SpinnerItem<Integer> colorModeItem = mColorModeSpinnerAdapter.getItem(position); mCurrPrintAttributes.setColorMode(colorModeItem.value); @@ -603,6 +614,10 @@ public class PrintJobConfigActivity extends Activity { mController.update(); } } else if (spinner == mOrientationSpinner) { + if (mIgnoreNextOrientationChange) { + mIgnoreNextOrientationChange = false; + return; + } SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position); mCurrPrintAttributes.setOrientation(orientationItem.value); @@ -610,6 +625,10 @@ public class PrintJobConfigActivity extends Activity { mController.update(); } } else if (spinner == mRangeOptionsSpinner) { + if (mIgnoreNextRangeOptionChange) { + mIgnoreNextRangeOptionChange = false; + return; + } updateUi(); if (!hasErrors()) { mController.update(); @@ -636,6 +655,11 @@ public class PrintJobConfigActivity extends Activity { @Override public void afterTextChanged(Editable editable) { + if (mIgnoreNextCopiesChange) { + mIgnoreNextCopiesChange = false; + return; + } + final boolean hadErrors = hasErrors(); if (editable.length() == 0) { @@ -674,6 +698,11 @@ public class PrintJobConfigActivity extends Activity { @Override public void afterTextChanged(Editable editable) { + if (mIgnoreNextRangeChange) { + mIgnoreNextRangeChange = false; + return; + } + final boolean hadErrors = hasErrors(); String text = editable.toString(); @@ -716,9 +745,19 @@ public class PrintJobConfigActivity extends Activity { private int mEditorState; + private boolean mIgnoreNextDestinationChange; + private boolean mIgnoreNextMediaSizeChange; + private boolean mIgnoreNextColorModeChange; + private boolean mIgnoreNextOrientationChange; + private boolean mIgnoreNextRangeOptionChange; + private boolean mIgnoreNextCopiesChange; + private boolean mIgnoreNextRangeChange; + public Editor() { // Copies mCopiesEditText = (EditText) findViewById(R.id.copies_edittext); + mCopiesEditText.setText(String.valueOf(MIN_COPIES)); + mSpooler.setPrintJobCopiesNoPersistence(mPrintJobId, MIN_COPIES); mCopiesEditText.addTextChangedListener(mCopiesTextWatcher); mCopiesEditText.selectAll(); @@ -772,19 +811,12 @@ public class PrintJobConfigActivity extends Activity { rangeOptionsValues[i], rangeOptionsLabels[i])); } mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter); - mRangeOptionsSpinner.setSelection(0); - // Here is some voodoo to circumvent the weird behavior of AdapterView - // in which a selection listener may get a callback for an event that - // happened before the listener was registered. The reason for that is - // that the selection change is handled on the next layout pass. - Choreographer.getInstance().postCallbackDelayed(Choreographer.CALLBACK_TRAVERSAL, - new Runnable() { - @Override - public void run() { - mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - } - }, null, Choreographer.getFrameDelay() * 2); + if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) { + mIgnoreNextRangeOptionChange = true; + mRangeOptionsSpinner.setSelection(0); + } + // Preview button mPrintPreviewButton = (Button) findViewById(R.id.print_preview_button); mPrintPreviewButton.setOnClickListener(new OnClickListener() { @Override @@ -797,6 +829,7 @@ public class PrintJobConfigActivity extends Activity { } }); + // Print button mPrintButton = (Button) findViewById(R.id.print_button); mPrintButton.setOnClickListener(new OnClickListener() { @Override @@ -806,11 +839,16 @@ public class PrintJobConfigActivity extends Activity { mController.update(); } }); + + updateUi(); } public void initialize() { mEditorState = EDITOR_STATE_INITIALIZED; - mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION); + if (mDestinationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) { + mIgnoreNextDestinationChange = true; + mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION); + } } public boolean isCancelled() { @@ -900,35 +938,47 @@ public class PrintJobConfigActivity extends Activity { // Destination mDestinationSpinner.setEnabled(false); - mCopiesEditText.removeTextChangedListener(mCopiesTextWatcher); - mCopiesEditText.setText(String.valueOf(MIN_COPIES)); - mCopiesEditText.addTextChangedListener(mCopiesTextWatcher); + String minCopiesString = String.valueOf(MIN_COPIES); + if (!TextUtils.equals(mCopiesEditText.getText(), minCopiesString)) { + mIgnoreNextCopiesChange = true; + mCopiesEditText.setText(minCopiesString); + } mCopiesEditText.setEnabled(false); // Media size - mMediaSizeSpinner.setOnItemSelectedListener(null); - mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION); + if (mMediaSizeSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) { + mIgnoreNextMediaSizeChange = true; + mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION); + } mMediaSizeSpinner.setEnabled(false); // Color mode - mColorModeSpinner.setOnItemSelectedListener(null); - mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION); + if (mColorModeSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) { + mIgnoreNextColorModeChange = true; + mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION); + } mColorModeSpinner.setEnabled(false); // Orientation - mOrientationSpinner.setOnItemSelectedListener(null); - mOrientationSpinner.setSelection(AdapterView.INVALID_POSITION); + if (mOrientationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) { + mIgnoreNextOrientationChange = true; + mOrientationSpinner.setSelection(AdapterView.INVALID_POSITION); + } mOrientationSpinner.setEnabled(false); // Range - mRangeOptionsSpinner.setOnItemSelectedListener(null); - mRangeOptionsSpinner.setSelection(0); + if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) { + mIgnoreNextRangeOptionChange = true; + mRangeOptionsSpinner.setSelection(0); + } mRangeOptionsSpinner.setEnabled(false); mRangeTitle.setText(getString(R.string.label_pages, getString(R.string.page_count_unknown))); - mRangeEditText.removeTextChangedListener(mRangeTextWatcher); - mRangeEditText.setText(""); - mRangeEditText.addTextChangedListener(mRangeTextWatcher); + if (!TextUtils.equals(mRangeEditText.getText(), "")) { + mIgnoreNextRangeChange = true; + mRangeEditText.setText(""); + } + mRangeEditText.setEnabled(false); mRangeEditText.setVisibility(View.INVISIBLE); @@ -974,8 +1024,10 @@ public class PrintJobConfigActivity extends Activity { mMediaSizeSpinner.setEnabled(true); final int selectedMediaSizeIndex = Math.max(mediaSizes.indexOf( defaultAttributes.getMediaSize()), 0); - mMediaSizeSpinner.setOnItemSelectedListener(null); - mMediaSizeSpinner.setSelection(selectedMediaSizeIndex); + if (mMediaSizeSpinner.getSelectedItemPosition() != selectedMediaSizeIndex) { + mIgnoreNextMediaSizeChange = true; + mMediaSizeSpinner.setSelection(selectedMediaSizeIndex); + } } } @@ -1014,8 +1066,10 @@ public class PrintJobConfigActivity extends Activity { mColorModeSpinner.setEnabled(true); final int selectedColorModeIndex = Integer.numberOfTrailingZeros( (colorModes & defaultAttributes.getColorMode())); - mColorModeSpinner.setOnItemSelectedListener(null); - mColorModeSpinner.setSelection(selectedColorModeIndex); + if (mColorModeSpinner.getSelectedItemPosition() != selectedColorModeIndex) { + mIgnoreNextColorModeChange = true; + mColorModeSpinner.setSelection(selectedColorModeIndex); + } } } @@ -1055,8 +1109,11 @@ public class PrintJobConfigActivity extends Activity { mOrientationSpinner.setEnabled(true); final int selectedOrientationIndex = Integer.numberOfTrailingZeros( (orientations & defaultAttributes.getOrientation())); - mOrientationSpinner.setOnItemSelectedListener(null); - mOrientationSpinner.setSelection(selectedOrientationIndex); + if (mOrientationSpinner.getSelectedItemPosition() + != selectedOrientationIndex) { + mIgnoreNextOrientationChange = true; + mOrientationSpinner.setSelection(selectedOrientationIndex); + } } } @@ -1080,8 +1137,10 @@ public class PrintJobConfigActivity extends Activity { ? getString(R.string.page_count_unknown) : String.valueOf(pageCount))); } else { - mRangeOptionsSpinner.setOnItemSelectedListener(null); - mRangeOptionsSpinner.setSelection(0); + if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) { + mIgnoreNextRangeOptionChange = true; + mRangeOptionsSpinner.setSelection(0); + } mRangeOptionsSpinner.setEnabled(false); mRangeTitle.setText(getString(R.string.label_pages, getString(R.string.page_count_unknown))); @@ -1109,26 +1168,12 @@ public class PrintJobConfigActivity extends Activity { // Copies if (mCopiesEditText.getError() == null && TextUtils.isEmpty(mCopiesEditText.getText())) { + mIgnoreNextCopiesChange = true; mCopiesEditText.setText(String.valueOf(MIN_COPIES)); mCopiesEditText.selectAll(); mCopiesEditText.requestFocus(); } } - - // Here is some voodoo to circumvent the weird behavior of AdapterView - // in which a selection listener may get a callback for an event that - // happened before the listener was registered. The reason for that is - // that the selection change is handled on the next layout pass. - Choreographer.getInstance().postCallbackDelayed(Choreographer.CALLBACK_TRAVERSAL, - new Runnable() { - @Override - public void run() { - mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - } - }, null, Choreographer.getFrameDelay() * 2); } public void addPrinters(List<PrinterInfo> addedPrinters) { @@ -1146,7 +1191,7 @@ public class PrintJobConfigActivity extends Activity { } if (!duplicate) { mDestinationSpinnerAdapter.add(new SpinnerItem<PrinterInfo>( - addedPrinter, addedPrinter.getLabel())); + addedPrinter, addedPrinter.getId().getPrinterName())); } else { Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter); } @@ -1269,14 +1314,14 @@ public class PrintJobConfigActivity extends Activity { PrinterInfo printerInfo = getItem(position).value; TextView title = (TextView) convertView.findViewById(R.id.title); - title.setText(printerInfo.getLabel()); + title.setText(printerInfo.getId().getPrinterName()); try { TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle); PackageManager pm = getPackageManager(); PackageInfo packageInfo = pm.getPackageInfo( - printerInfo.getId().getService().getPackageName(), 0); + printerInfo.getId().getServiceName().getPackageName(), 0); subtitle.setText(packageInfo.applicationInfo.loadLabel(pm)); subtitle.setVisibility(View.VISIBLE); } catch (NameNotFoundException nnfe) { diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java index 870bfff..fabd68f 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java @@ -85,6 +85,8 @@ public class PrintSpooler { private final PersistenceManager mPersistanceManager; + private final NotificationController mNotificationController; + private final Handler mHandler; private final Context mContext; @@ -103,6 +105,7 @@ public class PrintSpooler { private PrintSpooler(Context context) { mContext = context; mPersistanceManager = new PersistenceManager(context); + mNotificationController = new NotificationController(context); mHandler = new MyHandler(context.getMainLooper()); } @@ -123,7 +126,7 @@ public class PrintSpooler { SomeArgs args = SomeArgs.obtain(); args.arg1 = mClient; args.arg2 = printers; - mHandler.obtainMessage(MyHandler.MSG_REQUEST_UPDATE_PRINTERS, + mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS, args).sendToTarget(); } } @@ -133,14 +136,14 @@ public class PrintSpooler { SomeArgs args = SomeArgs.obtain(); args.arg1 = mClient; args.arg2 = observer; - mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY, + mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY, args).sendToTarget(); } } public void stopPrinterDiscovery() { synchronized (mLock) { - mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY, + mHandler.obtainMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY, mClient).sendToTarget(); } } @@ -154,7 +157,7 @@ public class PrintSpooler { PrinterId printerId = printJob.getPrinterId(); final boolean sameComponent = (componentName == null || (printerId != null - && componentName.equals(printerId.getService()))); + && componentName.equals(printerId.getServiceName()))); final boolean sameAppId = appId == PrintManager.APP_ID_ANY || printJob.getAppId() == appId; final boolean sameState = (state == printJob.getState()) @@ -187,21 +190,6 @@ public class PrintSpooler { } } - public boolean cancelPrintJob(int printJobId, int appId) { - synchronized (mLock) { - PrintJobInfo printJob = getPrintJobInfo(printJobId, appId); - if (printJob != null) { - switch (printJob.getState()) { - case PrintJobInfo.STATE_CREATED: - case PrintJobInfo.STATE_QUEUED: { - setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED); - } return true; - } - } - return false; - } - } - public PrintJobInfo createPrintJob(CharSequence label, IPrintClient client, PrintAttributes attributes, int appId) { synchronized (mLock) { @@ -240,7 +228,7 @@ public class PrintSpooler { case PrintJobInfo.STATE_QUEUED: case PrintJobInfo.STATE_STARTED: { - ComponentName service = printJob.getPrinterId().getService(); + ComponentName service = printJob.getPrinterId().getServiceName(); List<PrintJobInfo> jobsPerService = activeJobsPerServiceMap.get(service); if (jobsPerService == null) { jobsPerService = new ArrayList<PrintJobInfo>(); @@ -250,7 +238,7 @@ public class PrintSpooler { } break; default: { - ComponentName service = printJob.getPrinterId().getService(); + ComponentName service = printJob.getPrinterId().getServiceName(); if (!activeJobsPerServiceMap.containsKey(service)) { activeJobsPerServiceMap.put(service, null); } @@ -275,7 +263,7 @@ public class PrintSpooler { SomeArgs args = SomeArgs.obtain(); args.arg1 = client; args.arg2 = new PrintJobInfo(printJob); - mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED, + mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED, args).sendToTarget(); } } @@ -283,13 +271,13 @@ public class PrintSpooler { SomeArgs args = SomeArgs.obtain(); args.arg1 = client; args.arg2 = service; - mHandler.obtainMessage(MyHandler.MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, + mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, args).sendToTarget(); } } if (allPrintJobsHandled) { - mHandler.obtainMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED, + mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED, client).sendToTarget(); } } @@ -373,7 +361,7 @@ public class PrintSpooler { } } - public boolean setPrintJobState(int printJobId, int state) { + public boolean setPrintJobState(int printJobId, int state, CharSequence error) { boolean success = false; synchronized (mLock) { @@ -382,9 +370,13 @@ public class PrintSpooler { } PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); - if (printJob != null && printJob.getState() < state) { + if (printJob != null) { success = true; + + final int oldState = printJob.getState(); printJob.setState(state); + printJob.setFailureReason(error); + mNotificationController.onPrintJobStateChanged(printJob, oldState); if (DEBUG_PRINT_JOB_LIFECYCLE) { Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob); @@ -405,18 +397,18 @@ public class PrintSpooler { return true; } - ComponentName service = printerId.getService(); + ComponentName service = printerId.getServiceName(); if (!hasActivePrintJobsForServiceLocked(service)) { SomeArgs args = SomeArgs.obtain(); args.arg1 = mClient; args.arg2 = service; mHandler.obtainMessage( - MyHandler.MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, + MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, args).sendToTarget(); } if (!hasActivePrintJobsLocked()) { - mHandler.obtainMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED, + mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED, mClient).sendToTarget(); } } break; @@ -425,7 +417,7 @@ public class PrintSpooler { SomeArgs args = SomeArgs.obtain(); args.arg1 = mClient; args.arg2 = new PrintJobInfo(printJob); - mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED, + mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED, args).sendToTarget(); } break; } @@ -455,7 +447,7 @@ public class PrintSpooler { for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = mPrintJobs.get(i); if (!isActiveState(printJob.getState()) - && printJob.getPrinterId().getService().equals(service)) { + && printJob.getPrinterId().getServiceName().equals(service)) { return true; } } @@ -569,8 +561,8 @@ public class PrintSpooler { private static final String ATTR_FITTING_MODE = "fittingMode"; private static final String ATTR_ORIENTATION = "orientation"; - private static final String ATTR_LOCAL_ID = "localId"; - private static final String ATTR_SERVICE = "service"; + private static final String ATTR_PRINTER_NAME = "printerName"; + private static final String ATTR_SERVICE_NAME = "serviceName"; private static final String ATTR_WIDTH_MILS = "widthMils"; private static final String ATTR_HEIGHT_MILS = "heightMils"; @@ -659,8 +651,8 @@ public class PrintSpooler { PrinterId printerId = printJob.getPrinterId(); if (printerId != null) { serializer.startTag(null, TAG_PRINTER_ID); - serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId()); - serializer.attribute(null, ATTR_SERVICE, printerId.getService() + serializer.attribute(null, ATTR_PRINTER_NAME, printerId.getPrinterName()); + serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName() .flattenToString()); serializer.endTag(null, TAG_PRINTER_ID); } @@ -811,17 +803,17 @@ public class PrintSpooler { parser.setInput(in, null); parseState(parser); } catch (IllegalStateException ise) { - Slog.w(LOG_TAG, "Failed parsing " + ise); + Slog.w(LOG_TAG, "Failed parsing ", ise); } catch (NullPointerException npe) { - Slog.w(LOG_TAG, "Failed parsing " + npe); + Slog.w(LOG_TAG, "Failed parsing ", npe); } catch (NumberFormatException nfe) { - Slog.w(LOG_TAG, "Failed parsing " + nfe); + Slog.w(LOG_TAG, "Failed parsing ", nfe); } catch (XmlPullParserException xppe) { - Slog.w(LOG_TAG, "Failed parsing " + xppe); + Slog.w(LOG_TAG, "Failed parsing ", xppe); } catch (IOException ioe) { - Slog.w(LOG_TAG, "Failed parsing " + ioe); + Slog.w(LOG_TAG, "Failed parsing ", ioe); } catch (IndexOutOfBoundsException iobe) { - Slog.w(LOG_TAG, "Failed parsing " + iobe); + Slog.w(LOG_TAG, "Failed parsing ", iobe); } finally { try { in.close(); @@ -867,16 +859,16 @@ public class PrintSpooler { printJob.setUserId(userId); String tag = parser.getAttributeValue(null, ATTR_TAG); printJob.setTag(tag); - String copies = parser.getAttributeValue(null, ATTR_TAG); + String copies = parser.getAttributeValue(null, ATTR_COPIES); printJob.setCopies(Integer.parseInt(copies)); parser.next(); skipEmptyTextTags(parser); if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) { - String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID); + String localId = parser.getAttributeValue(null, ATTR_PRINTER_NAME); ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue( - null, ATTR_SERVICE)); + null, ATTR_SERVICE_NAME)); printJob.setPrinterId(new PrinterId(service, localId)); parser.next(); skipEmptyTextTags(parser); @@ -1066,12 +1058,12 @@ public class PrintSpooler { } private final class MyHandler extends Handler { - public static final int MSG_START_PRINTER_DISCOVERY = 1; - public static final int MSG_STOP_PRINTER_DISCOVERY = 2; - public static final int MSG_PRINT_JOB_QUEUED = 3; - public static final int MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4; - public static final int MSG_ALL_PRINT_JOBS_HANDLED = 5; - public static final int MSG_REQUEST_UPDATE_PRINTERS = 6; + public static final int MSG_ON_START_PRINTER_DISCOVERY = 1; + public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 2; + public static final int MSG_ON_PRINT_JOB_QUEUED = 3; + public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4; + public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 5; + public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 6; public MyHandler(Looper looper) { super(looper, null, false); @@ -1081,7 +1073,7 @@ public class PrintSpooler { @SuppressWarnings("unchecked") public void handleMessage(Message message) { switch (message.what) { - case MSG_START_PRINTER_DISCOVERY: { + case MSG_ON_START_PRINTER_DISCOVERY: { SomeArgs args = (SomeArgs) message.obj; IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1; IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg2; @@ -1095,7 +1087,7 @@ public class PrintSpooler { } } break; - case MSG_STOP_PRINTER_DISCOVERY: { + case MSG_ON_STOP_PRINTER_DISCOVERY: { IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj; if (client != null) { try { @@ -1106,7 +1098,7 @@ public class PrintSpooler { } } break; - case MSG_PRINT_JOB_QUEUED: { + case MSG_ON_PRINT_JOB_QUEUED: { SomeArgs args = (SomeArgs) message.obj; IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1; PrintJobInfo printJob = (PrintJobInfo) args.arg2; @@ -1120,7 +1112,7 @@ public class PrintSpooler { } } break; - case MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: { + case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: { SomeArgs args = (SomeArgs) message.obj; IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1; ComponentName service = (ComponentName) args.arg2; @@ -1135,7 +1127,7 @@ public class PrintSpooler { } } break; - case MSG_ALL_PRINT_JOBS_HANDLED: { + case MSG_ON_ALL_PRINT_JOBS_HANDLED: { final IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj; // This has to run on the tread that is persisting the current state // since this call may result in the system unbinding from the spooler @@ -1154,7 +1146,7 @@ public class PrintSpooler { }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); } break; - case MSG_REQUEST_UPDATE_PRINTERS: { + case MSG_ON_REQUEST_UPDATE_PRINTERS: { SomeArgs args = (SomeArgs) message.obj; IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1; List<PrinterId> printerIds = (List<PrinterId>) args.arg2; diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index 5ff2aa6..58853f7 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -91,17 +91,6 @@ public final class PrintSpoolerService extends Service { } } - @Override - public void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback, - int appId, int sequence) throws RemoteException { - boolean success = false; - try { - success = mSpooler.cancelPrintJob(printJobId, appId); - } finally { - callback.onCancelPrintJobResult(success, sequence); - } - } - @SuppressWarnings("deprecation") @Override public void createPrintJob(String printJobName, IPrintClient client, @@ -135,14 +124,11 @@ public final class PrintSpoolerService extends Service { } @Override - public void setPrintJobState(int printJobId, int state, - IPrintSpoolerCallbacks callback, int sequece) - throws RemoteException { + public void setPrintJobState(int printJobId, int state, CharSequence error, + IPrintSpoolerCallbacks callback, int sequece) throws RemoteException { boolean success = false; try { - // TODO: Make sure the clients (print services) can set the state - // only to acceptable ones, e.g. not settings STATE_CREATED. - success = mSpooler.setPrintJobState(printJobId, state); + success = mSpooler.setPrintJobState(printJobId, state, error); } finally { callback.onSetPrintJobStateResult(success, sequece); } diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java index 86e7685..41399d8 100644 --- a/services/java/com/android/server/print/PrintManagerService.java +++ b/services/java/com/android/server/print/PrintManagerService.java @@ -144,22 +144,45 @@ public final class PrintManagerService extends IPrintManager.Stub { } final long identity = Binder.clearCallingIdentity(); try { - if (spooler.cancelPrintJob(printJobId, resolvedAppId)) { - return; - } PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId); if (printJobInfo == null) { return; } - ComponentName printServiceName = printJobInfo.getPrinterId().getService(); - RemotePrintService printService = null; - synchronized (mLock) { - printService = userState.getActiveServices().get(printServiceName); + if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { + ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName(); + RemotePrintService printService = null; + synchronized (mLock) { + printService = userState.getActiveServices().get(printServiceName); + } + if (printService == null) { + return; + } + printService.onRequestCancelPrintJob(printJobInfo); + } else { + // If the print job is failed we do not need cooperation + // from the print service. + spooler.setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED, null); } - if (printService == null) { + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void restartPrintJob(int printJobId, int appId, int userId) { + final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final RemotePrintSpooler spooler; + synchronized (mLock) { + spooler = getOrCreateUserStateLocked(resolvedUserId).getSpoolerLocked(); + } + final long identity = Binder.clearCallingIdentity(); + try { + PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId); + if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { return; } - printService.onRequestCancelPrintJob(printJobInfo); + spooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java index 7acf6ab..5aa9952 100644 --- a/services/java/com/android/server/print/RemotePrintService.java +++ b/services/java/com/android/server/print/RemotePrintService.java @@ -423,12 +423,12 @@ final class RemotePrintService implements DeathRecipient { } @Override - public boolean setPrintJobState(int printJobId, int state) { + public boolean setPrintJobState(int printJobId, int state, CharSequence error) { RemotePrintService service = mWeakService.get(); if (service != null) { final long identity = Binder.clearCallingIdentity(); try { - return service.mSpooler.setPrintJobState(printJobId, state); + return service.mSpooler.setPrintJobState(printJobId, state, error); } finally { Binder.restoreCallingIdentity(identity); } @@ -524,8 +524,8 @@ final class RemotePrintService implements DeathRecipient { } private void throwIfPrinterIdTampered(PrinterId printerId) { - if (printerId == null || printerId.getService() == null - || !printerId.getService().equals(mComponentName)) { + if (printerId == null || printerId.getServiceName() == null + || !printerId.getServiceName().equals(mComponentName)) { throw new IllegalArgumentException("Invalid printer id: " + printerId); } } diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java index fe5b067..4e561bb 100644 --- a/services/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/java/com/android/server/print/RemotePrintSpooler.java @@ -66,8 +66,6 @@ final class RemotePrintSpooler { private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller(); - private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller(); - private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller(); private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller(); @@ -163,31 +161,6 @@ final class RemotePrintSpooler { return null; } - public final boolean cancelPrintJob(int printJobId, int appId) { - throwIfCalledOnMainThread(); - synchronized (mLock) { - throwIfDestroyedLocked(); - mCanUnbind = false; - } - if (DEBUG) { - Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] cancelPrintJob()"); - } - try { - return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstanceLazy(), - printJobId, appId); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error canceling print job.", re); - } catch (TimeoutException te) { - Slog.e(LOG_TAG, "Error canceling print job.", te); - } finally { - synchronized (mLock) { - mCanUnbind = true; - mLock.notifyAll(); - } - } - return false; - } - public final void writePrintJobData(ParcelFileDescriptor fd, int printJobId) { throwIfCalledOnMainThread(); synchronized (mLock) { @@ -239,7 +212,7 @@ final class RemotePrintSpooler { return null; } - public final boolean setPrintJobState(int printJobId, int state) { + public final boolean setPrintJobState(int printJobId, int state, CharSequence error) { throwIfCalledOnMainThread(); synchronized (mLock) { throwIfDestroyedLocked(); @@ -250,7 +223,7 @@ final class RemotePrintSpooler { } try { return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(), - printJobId, state); + printJobId, state, error); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error setting print job state.", re); } catch (TimeoutException te) { @@ -486,27 +459,6 @@ final class RemotePrintSpooler { } } - private static final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> { - private final IPrintSpoolerCallbacks mCallback; - - public CancelPrintJobCaller() { - super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); - mCallback = new BasePrintSpoolerServiceCallbacks() { - @Override - public void onCancelPrintJobResult(boolean canceled, int sequence) { - onRemoteMethodResult(canceled, sequence); - } - }; - } - - public boolean cancelPrintJob(IPrintSpooler target, int printJobId, - int appId) throws RemoteException, TimeoutException { - final int sequence = onBeforeRemoteCall(); - target.cancelPrintJob(printJobId, mCallback, appId, sequence); - return getResultTimed(sequence); - } - } - private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> { private final IPrintSpoolerCallbacks mCallback; @@ -542,9 +494,9 @@ final class RemotePrintSpooler { } public boolean setPrintJobState(IPrintSpooler target, int printJobId, - int status) throws RemoteException, TimeoutException { + int status, CharSequence error) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); - target.setPrintJobState(printJobId, status, mCallback, sequence); + target.setPrintJobState(printJobId, status, error, mCallback, sequence); return getResultTimed(sequence); } } diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index c41f9b0..c4fe124 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -85,7 +85,7 @@ final class UserState implements PrintSpoolerCallbacks { final RemotePrintService service; synchronized (mLock) { throwIfDestroyedLocked(); - ComponentName printServiceName = printJob.getPrinterId().getService(); + ComponentName printServiceName = printJob.getPrinterId().getServiceName(); service = mActiveServices.get(printServiceName); } if (service != null) { @@ -147,7 +147,7 @@ final class UserState implements PrintSpoolerCallbacks { if (mActiveServices.isEmpty()) { return; } - service = mActiveServices.get(printerIds.get(0).getService()); + service = mActiveServices.get(printerIds.get(0).getServiceName()); } if (service != null) { service.onRequestUpdatePrinters(printerIds); |