diff options
author | Svetoslav Ganov <svetoslavganov@google.com> | 2013-09-26 19:22:19 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-09-26 19:22:20 +0000 |
commit | 5cab967bf2bab49777bfa86dff8b0f892192ede6 (patch) | |
tree | 87e70842ccb310021f207c821df7fdbc25e541c1 | |
parent | 3adc49c6780633b8bccc3b5b8dca10788c2288bc (diff) | |
parent | 704697b6197262678e930daa831a1916ddee4dcf (diff) | |
download | frameworks_base-5cab967bf2bab49777bfa86dff8b0f892192ede6.zip frameworks_base-5cab967bf2bab49777bfa86dff8b0f892192ede6.tar.gz frameworks_base-5cab967bf2bab49777bfa86dff8b0f892192ede6.tar.bz2 |
Merge "Adding hidden APIs for observing the print jobs state." into klp-dev
20 files changed, 559 insertions, 63 deletions
@@ -166,6 +166,7 @@ LOCAL_SRC_FILES += \ core/java/android/print/IPrinterDiscoveryObserver.aidl \ core/java/android/print/IPrintDocumentAdapter.aidl \ core/java/android/print/IPrintClient.aidl \ + core/java/android/print/IPrintJobStateChangeListener.aidl \ core/java/android/print/IPrintManager.aidl \ core/java/android/print/IPrintSpooler.aidl \ core/java/android/print/IPrintSpoolerCallbacks.aidl \ diff --git a/api/current.txt b/api/current.txt index c2dfa92..21ca3c0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -19372,6 +19372,13 @@ package android.print { method public void cancel(); method public android.print.PrintJobId getId(); method public android.print.PrintJobInfo getInfo(); + method public boolean isBlocked(); + method public boolean isCancelled(); + method public boolean isCompleted(); + method public boolean isFailed(); + method public boolean isQueued(); + method public boolean isStarted(); + method public void restart(); } public final class PrintJobId implements android.os.Parcelable { @@ -19384,6 +19391,7 @@ package android.print { method public int describeContents(); method public android.print.PrintAttributes getAttributes(); method public int getCopies(); + method public long getCreationTime(); method public android.print.PrintJobId getId(); method public java.lang.String getLabel(); method public android.print.PageRange[] getPages(); diff --git a/core/java/android/print/IPrintJobStateChangeListener.aidl b/core/java/android/print/IPrintJobStateChangeListener.aidl new file mode 100644 index 0000000..c1d39f0 --- /dev/null +++ b/core/java/android/print/IPrintJobStateChangeListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print; + +import android.print.PrintJobId; + +/** + * Interface for observing print job state changes. + * + * @hide + */ +oneway interface IPrintJobStateChangeListener { + void onPrintJobStateChanged(in PrintJobId printJobId); +} diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index 4e839c6..4044b31 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -20,6 +20,7 @@ import android.print.IPrinterDiscoveryObserver; import android.print.IPrintDocumentAdapter; import android.print.IPrintClient; import android.print.PrintJobId; +import android.print.IPrintJobStateChangeListener; import android.print.PrinterId; import android.print.PrintJobInfo; import android.print.PrintAttributes; @@ -39,6 +40,11 @@ interface IPrintManager { void cancelPrintJob(in PrintJobId printJobId, int appId, int userId); void restartPrintJob(in PrintJobId printJobId, int appId, int userId); + void addPrintJobStateChangeListener(in IPrintJobStateChangeListener listener, + int appId, int userId); + void removePrintJobStateChangeListener(in IPrintJobStateChangeListener listener, + int userId); + List<PrintServiceInfo> getEnabledPrintServices(int userId); void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl index 8b511d6..0cf00cc 100644 --- a/core/java/android/print/IPrintSpoolerClient.aidl +++ b/core/java/android/print/IPrintSpoolerClient.aidl @@ -18,7 +18,7 @@ package android.print; import android.content.ComponentName; import android.print.PrintJobInfo; - +import android.print.PrintJobId; /** * Interface for receiving interesting state updates from the print spooler. @@ -29,4 +29,5 @@ oneway interface IPrintSpoolerClient { void onPrintJobQueued(in PrintJobInfo printJob); void onAllPrintJobsForServiceHandled(in ComponentName printService); void onAllPrintJobsHandled(); + void onPrintJobStateChanged(in PrintJobId printJobId, int appId); } diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index 00ade07..535ae43 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -62,14 +62,110 @@ public final class PrintJob { } /** - * Cancels this print job. + * Cancels this print job. You can request cancellation of a + * queued, started, blocked, or failed print job. + * + * @see #isQueued() + * @see #isStarted() + * @see #isBlocked() + * @see #isFailed() */ public void cancel() { - if (!isInImmutableState()) { + final int state = getInfo().getState(); + if (state == PrintJobInfo.STATE_QUEUED + || state == PrintJobInfo.STATE_STARTED + || state == PrintJobInfo.STATE_BLOCKED + || state == PrintJobInfo.STATE_FAILED) { mPrintManager.cancelPrintJob(mCachedInfo.getId()); } } + /** + * Restarts this print job. You can request restart of a failed + * print job. + * + * @see #isFailed() + */ + public void restart() { + if (isFailed()) { + mPrintManager.restartPrintJob(mCachedInfo.getId()); + } + } + + /** + * Gets whether this print job is queued. Such a print job is + * ready to be printed. You can request a cancellation via + * {@link #cancel()}. + * + * @return Whether the print job is queued. + * + * @see #cancel() + */ + public boolean isQueued() { + return getInfo().getState() == PrintJobInfo.STATE_QUEUED; + } + + /** + * Gets whether this print job is started. Such a print job is + * being printed. You can request a cancellation via + * {@link #cancel()}. + * + * @return Whether the print job is started. + * + * @see #cancel() + */ + public boolean isStarted() { + return getInfo().getState() == PrintJobInfo.STATE_STARTED; + } + + /** + * Gets whether this print job is blocked. Such a print job is halted + * due to an abnormal condition. You can request a cancellation via + * {@link #cancel()}. + * + * @return Whether the print job is blocked. + * + * @see #cancel() + */ + public boolean isBlocked() { + return getInfo().getState() == PrintJobInfo.STATE_BLOCKED; + } + + /** + * Gets whether this print job is completed. Such a print job + * is successfully printed. You can neither cancel nor restart + * such a print job. + * + * @return Whether the print job is completed. + */ + public boolean isCompleted() { + return getInfo().getState() == PrintJobInfo.STATE_COMPLETED; + } + + /** + * Gets whether this print job is failed. Such a print job is + * not successfully printed due to an error. You can request + * a restart via {@link #restart()}. + * + * @return Whether the print job is failed. + * + * @see #restart() + */ + public boolean isFailed() { + return getInfo().getState() == PrintJobInfo.STATE_FAILED; + } + + /** + * Gets whether this print job is cancelled. Such a print job was + * cancelled as a result of a user request. This is a final state. + * You cannot restart such a print job. + * + * @return Whether the print job is cancelled. + */ + public boolean isCancelled() { + return getInfo().getState() == PrintJobInfo.STATE_CANCELED; + } + private boolean isInImmutableState() { final int state = mCachedInfo.getState(); return state == PrintJobInfo.STATE_COMPLETED diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 502a9f2..e5d06a2 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -138,6 +138,9 @@ public final class PrintJobInfo implements Parcelable { /** Optional tag assigned by a print service.*/ private String mTag; + /** The wall time when the print job was created. */ + private long mCreationTime; + /** How many copies to print. */ private int mCopies; @@ -168,6 +171,7 @@ public final class PrintJobInfo implements Parcelable { mAppId = other.mAppId; mUserId = other.mUserId; mTag = other.mTag; + mCreationTime = other.mCreationTime; mCopies = other.mCopies; mStateReason = other.mStateReason; mPageRanges = other.mPageRanges; @@ -184,6 +188,7 @@ public final class PrintJobInfo implements Parcelable { mAppId = parcel.readInt(); mUserId = parcel.readInt(); mTag = parcel.readString(); + mCreationTime = parcel.readLong(); mCopies = parcel.readInt(); mStateReason = parcel.readString(); if (parcel.readInt() == 1) { @@ -368,6 +373,29 @@ public final class PrintJobInfo implements Parcelable { } /** + * Gets the wall time in millisecond when this print job was created. + * + * @return The creation time in milliseconds. + */ + public long getCreationTime() { + return mCreationTime; + } + + /** + * Sets the wall time in milliseconds when this print job was created. + * + * @param creationTime The creation time in milliseconds. + * + * @hide + */ + public void setCreationTime(long creationTime) { + if (creationTime < 0) { + throw new IllegalArgumentException("creationTime must be non-negative."); + } + mCreationTime = creationTime; + } + + /** * Gets the number of copies. * * @return The number of copies or zero if not set. @@ -491,6 +519,7 @@ public final class PrintJobInfo implements Parcelable { parcel.writeInt(mAppId); parcel.writeInt(mUserId); parcel.writeString(mTag); + parcel.writeLong(mCreationTime); parcel.writeInt(mCopies); parcel.writeString(mStateReason); if (mPageRanges != null) { @@ -522,6 +551,7 @@ public final class PrintJobInfo implements Parcelable { builder.append(", status: ").append(stateToString(mState)); builder.append(", printer: " + mPrinterId); builder.append(", tag: ").append(mTag); + builder.append(", creationTime: " + mCreationTime); builder.append(", copies: ").append(mCopies); builder.append(", attributes: " + (mAttributes != null ? mAttributes.toString() : null)); @@ -537,7 +567,7 @@ public final class PrintJobInfo implements Parcelable { public static String stateToString(int state) { switch (state) { case STATE_CREATED: { - return "STATUS_CREATED"; + return "STATE_CREATED"; } case STATE_QUEUED: { return "STATE_QUEUED"; @@ -546,21 +576,20 @@ public final class PrintJobInfo implements Parcelable { return "STATE_STARTED"; } case STATE_FAILED: { - return "STATUS_FAILED"; + return "STATE_FAILED"; } case STATE_COMPLETED: { - return "STATUS_COMPLETED"; + return "STATE_COMPLETED"; } case STATE_CANCELED: { - return "STATUS_CANCELED"; + return "STATE_CANCELED"; } default: { - return "STATUS_UNKNOWN"; + return "STATE_UNKNOWN"; } } } - public static final Parcelable.Creator<PrintJobInfo> CREATOR = new Creator<PrintJobInfo>() { @Override diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 5429155..a015388 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -30,6 +30,7 @@ import android.print.PrintDocumentAdapter.LayoutResultCallback; import android.print.PrintDocumentAdapter.WriteResultCallback; import android.printservice.PrintServiceInfo; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import com.android.internal.os.SomeArgs; @@ -40,6 +41,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; /** * System level service for accessing the printing capabilities of the platform. @@ -70,6 +72,19 @@ public final class PrintManager { private final Handler mHandler; + private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper> mPrintJobStateChangeListeners; + + /** @hide */ + public interface PrintJobStateChangeListener { + + /** + * Callback notifying that a print job state changed. + * + * @param printJobId The print job id. + */ + public void onPrintJobsStateChanged(PrintJobId printJobId); + } + /** * Creates a new instance. * @@ -106,7 +121,6 @@ public final class PrintManager { * @param userId The user id for which to get all print jobs. * @return An instance if the caller has the permission to access * all print jobs, null otherwise. - * * @hide */ public PrintManager getGlobalPrintManagerForUser(int userId) { @@ -123,6 +137,75 @@ public final class PrintManager { } /** + * Adds a listener for observing the state of print jobs. + * + * @param listener The listener to add. + * + * @hide + */ + public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mPrintJobStateChangeListeners == null) { + mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener, + PrintJobStateChangeListenerWrapper>(); + } + PrintJobStateChangeListenerWrapper wrappedListener = + new PrintJobStateChangeListenerWrapper(listener); + try { + mService.addPrintJobStateChangeListener(wrappedListener, mAppId, mUserId); + mPrintJobStateChangeListeners.put(listener, wrappedListener); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error adding print job state change listener", re); + } + } + + /** + * Removes a listener for observing the state of print jobs. + * + * @param listener The listener to remove. + * + * @hide + */ + public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mPrintJobStateChangeListeners == null) { + return; + } + PrintJobStateChangeListenerWrapper wrappedListener = + mPrintJobStateChangeListeners.remove(listener); + if (wrappedListener == null) { + return; + } + if (mPrintJobStateChangeListeners.isEmpty()) { + mPrintJobStateChangeListeners = null; + } + try { + mService.removePrintJobStateChangeListener(wrappedListener, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error removing print job state change listener", re); + } + } + + /** + * Gets a print job given its id. + * + * @return The print job list. + * + * @see PrintJob + * + * @hide + */ + public PrintJob getPrintJob(PrintJobId printJobId) { + try { + PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId); + if (printJob != null) { + return new PrintJob(printJob, this); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error getting print job", re); + } + return null; + } + + /** * Gets the print jobs for this application. * * @return The print job list. @@ -155,6 +238,14 @@ public final class PrintManager { } } + void restartPrintJob(PrintJobId printJobId) { + try { + mService.restartPrintJob(printJobId, mAppId, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error restarting a print job: " + printJobId, re); + } + } + /** * Creates a print job for printing a {@link PrintDocumentAdapter} with default print * attributes. @@ -163,7 +254,6 @@ public final class PrintManager { * @param documentAdapter An adapter that emits the document to print. * @param attributes The default print job attributes. * @return The created print job on success or null on failure. - * * @see PrintJob */ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, @@ -220,11 +310,11 @@ public final class PrintManager { } @Override - public void startPrintJobConfigActivity(IntentSender intent) { + public void startPrintJobConfigActivity(IntentSender intent) { PrintManager manager = mWeakPrintManager.get(); if (manager != null) { SomeArgs args = SomeArgs.obtain(); - args.arg1 = manager.mContext; + args.arg1 = manager.mContext; args.arg2 = intent; manager.mHandler.obtainMessage(0, args).sendToTarget(); } @@ -271,7 +361,7 @@ public final class PrintManager { @Override public void write(PageRange[] pages, ParcelFileDescriptor fd, - IWriteResultCallback callback, int sequence) { + IWriteResultCallback callback, int sequence) { synchronized (mLock) { if (mLayoutOrWriteCancellation != null) { mLayoutOrWriteCancellation.cancel(); @@ -492,4 +582,21 @@ public final class PrintManager { } } } + + private static final class PrintJobStateChangeListenerWrapper extends + IPrintJobStateChangeListener.Stub { + private final WeakReference<PrintJobStateChangeListener> mWeakListener; + + public PrintJobStateChangeListenerWrapper(PrintJobStateChangeListener listener) { + mWeakListener = new WeakReference<PrintJobStateChangeListener>(listener); + } + + @Override + public void onPrintJobStateChanged(PrintJobId printJobId) { + PrintJobStateChangeListener listener = mWeakListener.get(); + if (listener != null) { + listener.onPrintJobsStateChanged(printJobId); + } + } + } } diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index a51e28b..ad79a38 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -302,7 +302,7 @@ public final class PrinterInfo implements Parcelable { private boolean isValidStatus(int status) { return (status == STATUS_IDLE - || status == STATUS_IDLE + || status == STATUS_BUSY || status == STATUS_UNAVAILABLE); } } diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index 2fcae6b..721e31e 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -175,7 +175,7 @@ public final class PrintJob { */ public boolean isCancelled() { PrintService.throwIfNotCalledOnMainThread(); - return getInfo().getState() == PrintJobInfo.STATE_FAILED; + return getInfo().getState() == PrintJobInfo.STATE_CANCELED; } /** diff --git a/packages/PrintSpooler/res/drawable-hdpi/stat_notify_error.png b/core/res/res/drawable-hdpi/ic_print_error.png Binary files differindex 7846a78..7846a78 100644 --- a/packages/PrintSpooler/res/drawable-hdpi/stat_notify_error.png +++ b/core/res/res/drawable-hdpi/ic_print_error.png diff --git a/packages/PrintSpooler/res/drawable-mdpi/stat_notify_error.png b/core/res/res/drawable-mdpi/ic_print_error.png Binary files differindex 44109eb..44109eb 100644 --- a/packages/PrintSpooler/res/drawable-mdpi/stat_notify_error.png +++ b/core/res/res/drawable-mdpi/ic_print_error.png diff --git a/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_error.png b/core/res/res/drawable-xhdpi/ic_print_error.png Binary files differindex c3faa42..c3faa42 100644 --- a/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_error.png +++ b/core/res/res/drawable-xhdpi/ic_print_error.png diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 57a4bb7..2e991d7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1037,6 +1037,7 @@ <java-symbol type="drawable" name="ic_media_stop" /> <java-symbol type="drawable" name="ic_text_dot" /> <java-symbol type="drawable" name="ic_print" /> + <java-symbol type="drawable" name="ic_print_error" /> <java-symbol type="drawable" name="indicator_code_lock_drag_direction_green_up" /> <java-symbol type="drawable" name="indicator_code_lock_drag_direction_red_up" /> <java-symbol type="drawable" name="indicator_code_lock_point_area_default_holo" /> diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml index 65d1ee7..c3c5021 100644 --- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml +++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml @@ -45,7 +45,7 @@ android:ellipsize="end" android:textIsSelectable="false" android:visibility="gone" - android:textColor="@color/item_text_color" + android:textColor="@color/print_option_title" android:duplicateParentState="true"> </TextView> diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java index 25bb071..dae7770 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java +++ b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java @@ -32,7 +32,7 @@ import android.print.IPrintManager; import android.print.PrintJobId; import android.print.PrintJobInfo; import android.print.PrintManager; -import android.text.TextUtils; +import android.provider.Settings; import android.util.Log; /** @@ -47,9 +47,9 @@ public class 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 static final String INTENT_EXTRA_PRINTJOB_LABEL = "INTENT_EXTRA_PRINTJOB_LABEL"; - private static final String INTENT_EXTRA_PRINTER_NAME = "INTENT_EXTRA_PRINTER_NAME"; + private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID"; + private static final String EXTRA_PRINTJOB_LABEL = "EXTRA_PRINTJOB_LABEL"; + private static final String EXTRA_PRINTER_NAME = "EXTRA_PRINTER_NAME"; private final Context mContext; private final NotificationManager mNotificationManager; @@ -89,6 +89,7 @@ public class NotificationController { private void createPrintingNotification(PrintJobInfo printJob) { Notification.Builder builder = new Notification.Builder(mContext) + .setContentIntent(createContentIntent(printJob.getId())) .setSmallIcon(com.android.internal.R.drawable.ic_print) .setContentTitle(mContext.getString(R.string.printing_notification_title_template, printJob.getLabel())) @@ -102,18 +103,16 @@ public class NotificationController { } private void createFailedNotification(PrintJobInfo printJob) { - String reason = !TextUtils.isEmpty(printJob.getStateReason()) - ? printJob.getStateReason() : mContext.getString(R.string.reason_unknown); - Notification.Builder builder = new Notification.Builder(mContext) - .setSmallIcon(R.drawable.stat_notify_error) + .setContentIntent(createContentIntent(printJob.getId())) + .setSmallIcon(com.android.internal.R.drawable.ic_print_error) .setContentTitle(mContext.getString(R.string.failed_notification_title_template, printJob.getLabel())) .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel), createCancelIntent(printJob)) .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart), createRestartIntent(printJob.getId())) - .setContentText(reason) + .setContentText(printJob.getPrinterName()) .setWhen(System.currentTimeMillis()) .setOngoing(true) .setShowWhen(true); @@ -121,16 +120,14 @@ public class NotificationController { } private void createBlockedNotification(PrintJobInfo printJob) { - String reason = !TextUtils.isEmpty(printJob.getStateReason()) - ? printJob.getStateReason() : mContext.getString(R.string.reason_unknown); - Notification.Builder builder = new Notification.Builder(mContext) - .setSmallIcon(R.drawable.stat_notify_error) + .setContentIntent(createContentIntent(printJob.getId())) + .setSmallIcon(com.android.internal.R.drawable.ic_print_error) .setContentTitle(mContext.getString(R.string.blocked_notification_title_template, printJob.getLabel())) .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel), createCancelIntent(printJob)) - .setContentText(reason) + .setContentText(printJob.getPrinterName()) .setWhen(System.currentTimeMillis()) .setOngoing(true) .setShowWhen(true); @@ -141,19 +138,25 @@ public class NotificationController { mNotificationManager.cancel(printJobId.flattenToString(), 0); } + private PendingIntent createContentIntent(PrintJobId printJobId) { + Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS); + intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString()); + return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT); + } + private PendingIntent createCancelIntent(PrintJobInfo printJob) { Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class); intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString()); - intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJob.getId()); - intent.putExtra(INTENT_EXTRA_PRINTJOB_LABEL, printJob.getLabel()); - intent.putExtra(INTENT_EXTRA_PRINTER_NAME, printJob.getPrinterName()); + intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId()); + intent.putExtra(EXTRA_PRINTJOB_LABEL, printJob.getLabel()); + intent.putExtra(EXTRA_PRINTER_NAME, printJob.getPrinterName()); return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT); } private PendingIntent createRestartIntent(PrintJobId printJobId) { Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class); intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString()); - intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId); + intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId); return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT); } @@ -164,12 +167,12 @@ public class NotificationController { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) { - PrintJobId printJobId = intent.getExtras().getParcelable(INTENT_EXTRA_PRINTJOB_ID); - String printJobLabel = intent.getExtras().getString(INTENT_EXTRA_PRINTJOB_LABEL); - String printerName = intent.getExtras().getString(INTENT_EXTRA_PRINTER_NAME); + PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID); + String printJobLabel = intent.getExtras().getString(EXTRA_PRINTJOB_LABEL); + String printerName = intent.getExtras().getString(EXTRA_PRINTER_NAME); handleCancelPrintJob(context, printJobId, printJobLabel, printerName); } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) { - PrintJobId printJobId = intent.getExtras().getParcelable(INTENT_EXTRA_PRINTJOB_ID); + PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID); handleRestartPrintJob(context, printJobId); } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index ce1f6ec..62b35fe 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -155,23 +155,33 @@ public final class PrintSpoolerService extends Service { @SuppressWarnings("deprecation") @Override public void createPrintJob(PrintJobInfo printJob, IPrintClient client, - IPrintDocumentAdapter printAdapter) throws RemoteException { - PrintSpoolerService.this.createPrintJob(printJob); - - Intent intent = new Intent(printJob.getId().flattenToString()); - intent.setClass(PrintSpoolerService.this, PrintJobConfigActivity.class); - intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_DOCUMENT_ADAPTER, - printAdapter.asBinder()); - intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_JOB, printJob); - - IntentSender sender = PendingIntent.getActivity( - PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); - - Message message = mHandlerCaller.obtainMessageOO( - HandlerCallerCallback.MSG_START_PRINT_JOB_CONFIG_ACTIVITY, - client, sender); - mHandlerCaller.executeOrSendMessage(message); + IPrintDocumentAdapter printAdapter) throws RemoteException { + PrintSpoolerService.this.createPrintJob(printJob); + + Intent intent = new Intent(printJob.getId().flattenToString()); + intent.setClass(PrintSpoolerService.this, PrintJobConfigActivity.class); + intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_DOCUMENT_ADAPTER, + printAdapter.asBinder()); + intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_JOB, printJob); + + IntentSender sender = PendingIntent.getActivity( + PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); + + Message message = mHandlerCaller.obtainMessageIIO( + HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, + printJob.getAppId(), 0, printJob.getId()); + mHandlerCaller.executeOrSendMessage(message); + + message = mHandlerCaller.obtainMessageOO( + HandlerCallerCallback.MSG_START_PRINT_JOB_CONFIG_ACTIVITY, + client, sender); + mHandlerCaller.executeOrSendMessage(message); + + printJob.setCreationTime(System.currentTimeMillis()); + synchronized (mLock) { + mPersistanceManager.writeStateLocked(); + } } @Override @@ -240,12 +250,13 @@ public final class PrintSpoolerService extends Service { } private final class HandlerCallerCallback implements HandlerCaller.Callback { - public static final int MSG_SET_CLIENT = 9; - public static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 10; - public static final int MSG_ON_PRINT_JOB_QUEUED = 11; - public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 12; - public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 13; - public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 14; + public static final int MSG_SET_CLIENT = 1; + public static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 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_CHECK_ALL_PRINTJOBS_HANDLED = 6; + public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 7; @Override public void executeMessage(Message message) { @@ -310,6 +321,18 @@ public final class PrintSpoolerService extends Service { case MSG_CHECK_ALL_PRINTJOBS_HANDLED: { checkAllPrintJobsHandled(); } break; + + case MSG_ON_PRINT_JOB_STATE_CHANGED: { + if (mClient != null) { + PrintJobId printJobId = (PrintJobId) message.obj; + final int appId = message.arg1; + try { + mClient.onPrintJobStateChanged(printJobId, appId); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error notify for print job state change.", re); + } + } + } break; } } } @@ -511,6 +534,11 @@ public final class PrintSpoolerService extends Service { synchronized (mLock) { PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); if (printJob != null) { + final int oldState = printJob.getState(); + if (oldState == state) { + return false; + } + success = true; printJob.setState(state); @@ -553,6 +581,11 @@ public final class PrintSpoolerService extends Service { if (!hasActivePrintJobsLocked()) { notifyOnAllPrintJobsHandled(); } + + Message message = mHandlerCaller.obtainMessageIIO( + HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, + printJob.getAppId(), 0, printJob.getId()); + mHandlerCaller.executeOrSendMessage(message); } } @@ -706,7 +739,10 @@ public final class PrintSpoolerService extends Service { private static final String ATTR_APP_ID = "appId"; private static final String ATTR_USER_ID = "userId"; private static final String ATTR_TAG = "tag"; + private static final String ATTR_CREATION_TIME = "creationTime"; private static final String ATTR_COPIES = "copies"; + private static final String ATTR_PRINTER_NAME = "printerName"; + private static final String ATTR_STATE_REASON = "stateReason"; private static final String TAG_MEDIA_SIZE = "mediaSize"; private static final String TAG_RESOLUTION = "resolution"; @@ -714,7 +750,7 @@ public final class PrintSpoolerService extends Service { private static final String ATTR_COLOR_MODE = "colorMode"; - private static final String ATTR_LOCAL_ID = "printerName"; + private static final String ATTR_LOCAL_ID = "localId"; private static final String ATTR_SERVICE_NAME = "serviceName"; private static final String ATTR_WIDTH_MILS = "widthMils"; @@ -794,7 +830,17 @@ public final class PrintSpoolerService extends Service { if (tag != null) { serializer.attribute(null, ATTR_TAG, tag); } + serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf( + printJob.getCreationTime())); serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies())); + String printerName = printJob.getPrinterName(); + if (!TextUtils.isEmpty(printerName)) { + serializer.attribute(null, ATTR_PRINTER_NAME, printerName); + } + String stateReason = printJob.getStateReason(); + if (!TextUtils.isEmpty(stateReason)) { + serializer.attribute(null, ATTR_STATE_REASON, stateReason); + } PrinterId printerId = printJob.getPrinterId(); if (printerId != null) { @@ -979,8 +1025,14 @@ public final class PrintSpoolerService extends Service { printJob.setUserId(userId); String tag = parser.getAttributeValue(null, ATTR_TAG); printJob.setTag(tag); + String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME); + printJob.setCreationTime(Long.parseLong(creationTime)); String copies = parser.getAttributeValue(null, ATTR_COPIES); printJob.setCopies(Integer.parseInt(copies)); + String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME); + printJob.setPrinterName(printerName); + String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON); + printJob.setStateReason(stateReason); parser.next(); diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java index 5f8708a..625770c 100644 --- a/services/java/com/android/server/print/PrintManagerService.java +++ b/services/java/com/android/server/print/PrintManagerService.java @@ -32,9 +32,11 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Process; +import android.os.RemoteException; import android.os.UserHandle; import android.print.IPrintClient; import android.print.IPrintDocumentAdapter; +import android.print.IPrintJobStateChangeListener; import android.print.IPrintManager; import android.print.IPrinterDiscoveryObserver; import android.print.PrintAttributes; @@ -300,6 +302,39 @@ public final class PrintManagerService extends IPrintManager.Stub { } @Override + public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, + int appId, int userId) throws RemoteException { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.addPrintJobStateChangeListener(listener, resolvedAppId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener, + int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.removePrintJobStateChangeListener(listener); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java index 1bde6d7..f98a805 100644 --- a/services/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/java/com/android/server/print/RemotePrintSpooler.java @@ -91,6 +91,7 @@ final class RemotePrintSpooler { public static interface PrintSpoolerCallbacks { public void onPrintJobQueued(PrintJobInfo printJob); public void onAllPrintJobsForServiceHandled(ComponentName printService); + public void onPrintJobStateChanged(PrintJobId printJobId, int appId); } public RemotePrintSpooler(Context context, int userId, @@ -345,6 +346,10 @@ final class RemotePrintSpooler { } } + private void onPrintJobStateChanged(PrintJobId printJobId, int appId) { + mCallbacks.onPrintJobStateChanged(printJobId, appId); + } + private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException { synchronized (mLock) { if (mRemoteInstance != null) { @@ -618,5 +623,18 @@ final class RemotePrintSpooler { } } } + + @Override + public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { + RemotePrintSpooler spooler = mWeakSpooler.get(); + if (spooler != null) { + final long identity = Binder.clearCallingIdentity(); + try { + spooler.onPrintJobStateChanged(printJobId, appId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } } } diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index 8c21827..e5f5842 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -36,6 +36,7 @@ import android.os.RemoteException; import android.os.UserManager; import android.print.IPrintClient; import android.print.IPrintDocumentAdapter; +import android.print.IPrintJobStateChangeListener; import android.print.IPrinterDiscoveryObserver; import android.print.PrintAttributes; import android.print.PrintJobId; @@ -103,8 +104,12 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { private final RemotePrintSpooler mSpooler; + private final Handler mHandler; + private PrinterDiscoverySessionMediator mPrinterDiscoverySession; + private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords; + private boolean mDestroyed; public UserState(Context context, int userId, Object lock) { @@ -112,6 +117,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { mUserId = userId; mLock = lock; mSpooler = new RemotePrintSpooler(context, userId, this); + mHandler = new UserStateHandler(context.getMainLooper()); synchronized (mLock) { enableSystemPrintServicesOnFirstBootLocked(); } @@ -164,6 +170,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { printJob.setLabel(printJobName); printJob.setAttributes(attributes); printJob.setState(PrintJobInfo.STATE_CREATED); + printJob.setCopies(1); // Spin the spooler to add the job and show the config UI. new AsyncTask<Void, Void, Void>() { @@ -351,6 +358,51 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } + public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener, + int appId) throws RemoteException { + synchronized (mLock) { + throwIfDestroyedLocked(); + if (mPrintJobStateChangeListenerRecords == null) { + mPrintJobStateChangeListenerRecords = + new ArrayList<PrintJobStateChangeListenerRecord>(); + } + mPrintJobStateChangeListenerRecords.add( + new PrintJobStateChangeListenerRecord(listener, appId) { + @Override + public void onBinderDied() { + mPrintJobStateChangeListenerRecords.remove(this); + } + }); + } + } + + public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener) { + synchronized (mLock) { + throwIfDestroyedLocked(); + if (mPrintJobStateChangeListenerRecords == null) { + return; + } + final int recordCount = mPrintJobStateChangeListenerRecords.size(); + for (int i = 0; i < recordCount; i++) { + PrintJobStateChangeListenerRecord record = + mPrintJobStateChangeListenerRecords.get(i); + if (record.listener.asBinder().equals(listener.asBinder())) { + mPrintJobStateChangeListenerRecords.remove(i); + break; + } + } + if (mPrintJobStateChangeListenerRecords.isEmpty()) { + mPrintJobStateChangeListenerRecords = null; + } + } + } + + @Override + public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { + mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED, + appId, 0, printJobId).sendToTarget(); + } + @Override public void onPrintersAdded(List<PrinterInfo> printers) { synchronized (mLock) { @@ -698,6 +750,65 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } + private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) { + final List<PrintJobStateChangeListenerRecord> records; + synchronized (mLock) { + if (mPrintJobStateChangeListenerRecords == null) { + return; + } + records = new ArrayList<PrintJobStateChangeListenerRecord>( + mPrintJobStateChangeListenerRecords); + } + final int recordCount = records.size(); + for (int i = 0; i < recordCount; i++) { + PrintJobStateChangeListenerRecord record = records.get(i); + if (record.appId == PrintManager.APP_ID_ANY + || record.appId == appId) + try { + record.listener.onPrintJobStateChanged(printJobId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error notifying for print job state change", re); + } + } + } + + private final class UserStateHandler extends Handler { + public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1; + + public UserStateHandler(Looper looper) { + super(looper, null, false); + } + + @Override + public void handleMessage(Message message) { + if (message.what == MSG_DISPATCH_PRINT_JOB_STATE_CHANGED) { + PrintJobId printJobId = (PrintJobId) message.obj; + final int appId = message.arg1; + handleDispatchPrintJobStateChanged(printJobId, appId); + } + } + } + + private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { + final IPrintJobStateChangeListener listener; + final int appId; + + public PrintJobStateChangeListenerRecord(IPrintJobStateChangeListener listener, + int appId) throws RemoteException { + this.listener = listener; + this.appId = appId; + listener.asBinder().linkToDeath(this, 0); + } + + @Override + public void binderDied() { + listener.asBinder().unlinkToDeath(this, 0); + onBinderDied(); + } + + public abstract void onBinderDied(); + } + private class PrinterDiscoverySessionMediator { private final ArrayMap<PrinterId, PrinterInfo> mPrinters = new ArrayMap<PrinterId, PrinterInfo>(); |