diff --git a/api/current.txt b/api/current.txt index 99a6430f7237e..0177e4f29de7c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -30270,7 +30270,7 @@ package android.print { method public android.print.PrinterInfo build(); method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo); method public android.print.PrinterInfo.Builder setDescription(java.lang.String); - method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(); + method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean); method public android.print.PrinterInfo.Builder setIconResourceId(int); method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent); method public android.print.PrinterInfo.Builder setName(java.lang.String); @@ -30322,6 +30322,7 @@ package android.printservice { method public boolean isStarted(); method public void setProgress(float); method public void setStatus(java.lang.CharSequence); + method public void setStatus(int); method public boolean setTag(java.lang.String); method public boolean start(); } diff --git a/api/system-current.txt b/api/system-current.txt index d27923b625dec..7956826ee43e9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -32583,7 +32583,7 @@ package android.print { method public android.print.PrinterInfo build(); method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo); method public android.print.PrinterInfo.Builder setDescription(java.lang.String); - method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(); + method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean); method public android.print.PrinterInfo.Builder setIconResourceId(int); method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent); method public android.print.PrinterInfo.Builder setName(java.lang.String); @@ -32635,6 +32635,7 @@ package android.printservice { method public boolean isStarted(); method public void setProgress(float); method public void setStatus(java.lang.CharSequence); + method public void setStatus(int); method public boolean setTag(java.lang.String); method public boolean start(); } diff --git a/api/test-current.txt b/api/test-current.txt index 7f169956cf8b8..264b5eb4186e2 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -30262,7 +30262,7 @@ package android.print { method public android.print.PrinterId getPrinterId(); method public float getProgress(); method public int getState(); - method public java.lang.CharSequence getStatus(); + method public java.lang.CharSequence getStatus(android.content.pm.PackageManager); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int STATE_BLOCKED = 4; // 0x4 @@ -30339,7 +30339,7 @@ package android.print { method public android.print.PrinterInfo build(); method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo); method public android.print.PrinterInfo.Builder setDescription(java.lang.String); - method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(); + method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean); method public android.print.PrinterInfo.Builder setIconResourceId(int); method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent); method public android.print.PrinterInfo.Builder setName(java.lang.String); @@ -30391,6 +30391,7 @@ package android.printservice { method public boolean isStarted(); method public void setProgress(float); method public void setStatus(java.lang.CharSequence); + method public void setStatus(int); method public boolean setTag(java.lang.String); method public boolean start(); } diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl index 469a4ea812b53..63bf8857c188d 100644 --- a/core/java/android/print/IPrintSpooler.aidl +++ b/core/java/android/print/IPrintSpooler.aidl @@ -60,6 +60,15 @@ oneway interface IPrintSpooler { */ void setStatus(in PrintJobId printJobId, in CharSequence status); + /** + * Set the status of this print job + * + * @param printJobId The print job to update + * @param status The new status as a string resource + * @param appPackageName App package name the resource belongs to + */ + void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName); + /** * Handle that a custom icon for a printer was loaded. * diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 7e3a72feb2f0d..f134943598456 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -21,7 +21,10 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.annotation.TestApi; +import android.content.pm.PackageManager; +import android.content.res.Resources; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -181,7 +184,11 @@ public final class PrintJobInfo implements Parcelable { private float mProgress; /** A short string describing the status of this job. */ - private CharSequence mStatus; + private @Nullable CharSequence mStatus; + + /** A string resource describing the status of this job. */ + private @StringRes int mStatusRes; + private @Nullable CharSequence mStatusResAppPackageName; /** Advanced printer specific options. */ private Bundle mAdvancedOptions; @@ -210,6 +217,8 @@ public final class PrintJobInfo implements Parcelable { mDocumentInfo = other.mDocumentInfo; mProgress = other.mProgress; mStatus = other.mStatus; + mStatusRes = other.mStatusRes; + mStatusResAppPackageName = other.mStatusResAppPackageName; mCanceling = other.mCanceling; mAdvancedOptions = other.mAdvancedOptions; } @@ -235,8 +244,14 @@ public final class PrintJobInfo implements Parcelable { mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null); mProgress = parcel.readFloat(); mStatus = parcel.readCharSequence(); + mStatusRes = parcel.readInt(); + mStatusResAppPackageName = parcel.readCharSequence(); mCanceling = (parcel.readInt() == 1); mAdvancedOptions = parcel.readBundle(); + + if (mAdvancedOptions != null) { + Preconditions.checkArgument(!mAdvancedOptions.containsKey(null)); + } } /** @@ -370,9 +385,27 @@ public final class PrintJobInfo implements Parcelable { * @hide */ public void setStatus(@Nullable CharSequence status) { + mStatusRes = 0; + mStatusResAppPackageName = null; + mStatus = status; } + /** + * Sets the status of the print job. + * + * @param status The new status as a string resource + * @param appPackageName App package name the resource belongs to + * + * @hide + */ + public void setStatus(@StringRes int status, @NonNull CharSequence appPackageName) { + mStatus = null; + + mStatusRes = status; + mStatusResAppPackageName = appPackageName; + } + /** * Sets the owning application id. * @@ -633,6 +666,8 @@ public final class PrintJobInfo implements Parcelable { parcel.writeParcelable(mDocumentInfo, 0); parcel.writeFloat(mProgress); parcel.writeCharSequence(mStatus); + parcel.writeInt(mStatusRes); + parcel.writeCharSequence(mStatusResAppPackageName); parcel.writeInt(mCanceling ? 1 : 0); parcel.writeBundle(mAdvancedOptions); } @@ -659,6 +694,9 @@ public final class PrintJobInfo implements Parcelable { builder.append(", progress: " + mProgress); builder.append(", status: " + (mStatus != null ? mStatus.toString() : null)); + builder.append(", statusRes: " + mStatusRes); + builder.append(", statusResAppPackageName: " + (mStatusResAppPackageName != null + ? mStatusResAppPackageName.toString() : null)); builder.append("}"); return builder.toString(); } @@ -707,12 +745,23 @@ public final class PrintJobInfo implements Parcelable { /** * Get the status of this job. * + * @param pm Package manager used to resolve the string + * * @return the status of this job or null if not set * @hide */ @TestApi - public @Nullable CharSequence getStatus() { - return mStatus; + public @Nullable CharSequence getStatus(@NonNull PackageManager pm) { + if (mStatusRes == 0) { + return mStatus; + } else { + try { + return pm.getResourcesForApplication(mStatusResAppPackageName.toString()) + .getString(mStatusRes); + } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { + return null; + } + } } /** @@ -789,6 +838,8 @@ public final class PrintJobInfo implements Parcelable { * @param value The option value. */ public void putAdvancedOption(@NonNull String key, @Nullable String value) { + Preconditions.checkNotNull(key, "key cannot be null"); + if (mPrototype.mAdvancedOptions == null) { mPrototype.mAdvancedOptions = new Bundle(); } diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index 0d2d9f4946b6c..1ee6389ce9bae 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -467,10 +467,12 @@ public final class PrinterInfo implements Parcelable { * {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}. *

* + * @param hasCustomPrinterIcon If the printer has a custom icon or not. + * * @return This builder. */ - public @NonNull Builder setHasCustomPrinterIcon() { - mHasCustomPrinterIcon = true; + public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) { + mHasCustomPrinterIcon = hasCustomPrinterIcon; return this; } diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl index 0ae1e180ec9e8..f0ea6ae895fae 100644 --- a/core/java/android/printservice/IPrintServiceClient.aidl +++ b/core/java/android/printservice/IPrintServiceClient.aidl @@ -52,6 +52,15 @@ interface IPrintServiceClient { */ void setStatus(in PrintJobId printJobId, in CharSequence status); + /** + * Set the status of this print job + * + * @param printJobId The print job to update + * @param status The new status as a string resource + * @param appPackageName The app package name the string belongs to + */ + void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName); + void onPrintersAdded(in ParceledListSlice printers); void onPrintersRemoved(in ParceledListSlice printerIds); diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index 6414b6a2230c8..7a7ca2353b1d8 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -20,11 +20,14 @@ import android.annotation.FloatRange; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; +import android.content.Context; import android.os.RemoteException; import android.print.PrintJobId; import android.print.PrintJobInfo; import android.text.TextUtils; import android.util.Log; +import com.android.internal.util.Preconditions; /** * This class represents a print job from the perspective of a print @@ -45,7 +48,12 @@ public final class PrintJob { private PrintJobInfo mCachedInfo; - PrintJob(@NonNull PrintJobInfo jobInfo, @NonNull IPrintServiceClient client) { + /** Context that created the object */ + private final Context mContext; + + PrintJob(@NonNull Context context, @NonNull PrintJobInfo jobInfo, + @NonNull IPrintServiceClient client) { + mContext = context; mCachedInfo = jobInfo; mPrintServiceClient = client; mDocument = new PrintDocument(mCachedInfo.getId(), client, @@ -216,11 +224,10 @@ public final class PrintJob { } /** - * Blocks the print job. You should call this method if {@link - * #isStarted()} or {@link #isBlocked()} returns true and you need - * to block the print job. For example, the user has to add some - * paper to continue printing. To resume the print job call {@link - * #start()}. + * Blocks the print job. You should call this method if {@link #isStarted()} returns true and + * you need to block the print job. For example, the user has to add some paper to continue + * printing. To resume the print job call {@link #start()}. To change the reason call + * {@link #setStatus(CharSequence)}. * * @param reason The human readable, short, and translated reason why the print job is blocked. * @return Whether the job was blocked. @@ -233,9 +240,7 @@ public final class PrintJob { PrintService.throwIfNotCalledOnMainThread(); PrintJobInfo info = getInfo(); final int state = info.getState(); - if (state == PrintJobInfo.STATE_STARTED - || (state == PrintJobInfo.STATE_BLOCKED - && !TextUtils.equals(info.getStatus(), reason))) { + if (state == PrintJobInfo.STATE_STARTED || state == PrintJobInfo.STATE_BLOCKED) { return setState(PrintJobInfo.STATE_BLOCKED, reason); } return false; @@ -320,6 +325,9 @@ public final class PrintJob { /** * Sets the status of this print job. This should be a human readable, short, and translated * description of the current state of the print job. + *

+ * This overrides any previously set status set via {@link #setStatus(CharSequence)}, + * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)}, * * @param status The new status. If null the status will be empty. */ @@ -334,6 +342,29 @@ public final class PrintJob { } } + /** + * Sets the status of this print job as a string resource. + *

+ * This overrides any previously set status set via {@link #setStatus(CharSequence)}, + * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)}, + *

+ * To clear the status use {@link #setStatus(CharSequence) setStatus(null)} + * + * @param status The new status as a String resource. + */ + @MainThread + public void setStatus(@StringRes int status) { + PrintService.throwIfNotCalledOnMainThread(); + Preconditions.checkArgument(status != 0, "status has to be != 0"); + + try { + mPrintServiceClient.setStatusRes(mCachedInfo.getId(), status, + mContext.getPackageName()); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error setting status for job: " + mCachedInfo.getId(), re); + } + } + /** * Sets a tag that is valid in the context of a {@link PrintService} * and is not interpreted by the system. For example, a print service diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 62d214e9e43c9..8f73518eba0fd 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -329,7 +329,7 @@ public abstract class PrintService extends Service { final int printJobInfoCount = printJobInfos.size(); printJobs = new ArrayList(printJobInfoCount); for (int i = 0; i < printJobInfoCount; i++) { - printJobs.add(new PrintJob(printJobInfos.get(i), mClient)); + printJobs.add(new PrintJob(this, printJobInfos.get(i), mClient)); } } if (printJobs != null) { @@ -549,7 +549,7 @@ public abstract class PrintService extends Service { + getPackageName()); } PrintJobInfo printJobInfo = (PrintJobInfo) message.obj; - onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient)); + onRequestCancelPrintJob(new PrintJob(PrintService.this, printJobInfo, mClient)); } break; case MSG_ON_PRINTJOB_QUEUED: { @@ -561,7 +561,7 @@ public abstract class PrintService extends Service { if (DEBUG) { Log.i(LOG_TAG, "Queued: " + printJobInfo); } - onPrintJobQueued(new PrintJob(printJobInfo, mClient)); + onPrintJobQueued(new PrintJob(PrintService.this, printJobInfo, mClient)); } break; case MSG_SET_CLIENT: { diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 53cb56ebecd9d..c46851e1c757b 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -34,6 +34,20 @@ public class Preconditions { } } + /** + * Ensures that an expression checking an argument is true. + * + * @param expression the expression to check + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression, final Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + /** * Ensures that an string reference passed as a parameter to the calling * method is not empty. diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java index 0210693c302c8..cd1d540d922be 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java @@ -208,7 +208,7 @@ final class NotificationController { } } - CharSequence status = printJob.getStatus(); + CharSequence status = printJob.getStatus(mContext.getPackageManager()); if (status != null) { builder.setContentText(status); } else { diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java index d2b4d1b3d8777..9b1ab0e5f671e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java @@ -19,6 +19,7 @@ package com.android.printspooler.model; import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -575,19 +576,35 @@ public final class PrintSpoolerService extends Service { } } - /** - * Set the status for a print job. - * - * @param printJobId ID of the print job to update - * @param status the new status - */ - public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) { - synchronized (mLock) { - getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status); + /** + * Set the status for a print job. + * + * @param printJobId ID of the print job to update + * @param status the new status + */ + public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) { + synchronized (mLock) { + getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status); - mNotificationController.onUpdateNotifications(mPrintJobs); - } - } + mNotificationController.onUpdateNotifications(mPrintJobs); + } + } + + /** + * Set the status for a print job. + * + * @param printJobId ID of the print job to update + * @param status the new status as a string resource + * @param appPackageName app package the resource belongs to + */ + public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status, + @Nullable CharSequence appPackageName) { + synchronized (mLock) { + getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status, appPackageName); + + mNotificationController.onUpdateNotifications(mPrintJobs); + } + } public boolean hasActivePrintJobsLocked() { final int printJobCount = mPrintJobs.size(); @@ -884,7 +901,7 @@ public final class PrintSpoolerService extends Service { serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress)); } - CharSequence status = printJob.getStatus(); + CharSequence status = printJob.getStatus(getPackageManager()); if (!TextUtils.isEmpty(status)) { serializer.attribute(null, ATTR_STATUS, status.toString()); } @@ -1435,6 +1452,13 @@ public final class PrintSpoolerService extends Service { PrintSpoolerService.this.setStatus(printJobId, status); } + @Override + public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status, + @NonNull CharSequence appPackageName) throws RemoteException { + PrintSpoolerService.this.setStatus(printJobId, status, appPackageName); + } + + public PrintSpoolerService getService() { return PrintSpoolerService.this; } diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java index 9b99c677a8222..9c3a85295f413 100644 --- a/services/print/java/com/android/server/print/RemotePrintService.java +++ b/services/print/java/com/android/server/print/RemotePrintService.java @@ -19,6 +19,7 @@ package com.android.server.print; import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -812,6 +813,20 @@ final class RemotePrintService implements DeathRecipient { } } + @Override + public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status, + @NonNull CharSequence appPackageName) { + RemotePrintService service = mWeakService.get(); + if (service != null) { + final long identity = Binder.clearCallingIdentity(); + try { + service.mSpooler.setStatus(printJobId, status, appPackageName); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + @Override @SuppressWarnings({"rawtypes", "unchecked"}) public void onPrintersAdded(ParceledListSlice printers) { diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java index e1d8c6cdc3a13..be822c81f91b0 100644 --- a/services/print/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java @@ -19,6 +19,7 @@ package com.android.server.print; import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -300,6 +301,35 @@ final class RemotePrintSpooler { } } + /** + * Set status of a print job. + * + * @param printJobId The print job to update + * @param status The new status as a string resource + * @param appPackageName The app package name the string res belongs to + */ + public final void setStatus(@NonNull PrintJobId printJobId, @StringRes int status, + @NonNull CharSequence appPackageName) { + throwIfCalledOnMainThread(); + synchronized (mLock) { + throwIfDestroyedLocked(); + mCanUnbind = false; + } + try { + getRemoteInstanceLazy().setStatusRes(printJobId, status, appPackageName); + } catch (RemoteException|TimeoutException re) { + Slog.e(LOG_TAG, "Error setting status.", re); + } finally { + if (DEBUG) { + Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()"); + } + synchronized (mLock) { + mCanUnbind = true; + mLock.notifyAll(); + } + } + } + /** * Handle that a custom icon for a printer was loaded. *