Change hasCustomPrintIcon and setStatus as requested by API council.
Bonus: null advanced keys produced a exception deep in the print
spooler. Hence prevent null keys on the surface from now on.
Bug: 27716355
Change-Id: I3c064956f4e670cd7091437ade06605aa8d797b0
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 469a4ea..63bf885 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -61,6 +61,15 @@
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.
*
* @param printerId the id of the printer the icon belongs to
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 7e3a72f..f134943 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -21,7 +21,10 @@
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 @@
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 @@
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 @@
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,10 +385,28 @@
* @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.
*
* @return The owning app id.
@@ -633,6 +666,8 @@
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 @@
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 @@
/**
* 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 @@
* @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 0d2d9f4..1ee6389 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -467,10 +467,12 @@
* {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}.
* </p>
*
+ * @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 0ae1e18..f0ea6ae 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -52,6 +52,15 @@
*/
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 6414b6a..7a7ca23 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -20,11 +20,14 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
/**
* 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.
+ * <p />
+ * 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.
*/
@@ -335,6 +343,29 @@
}
/**
+ * Sets the status of this print job as a string resource.
+ * <p />
+ * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+ * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
+ * <p />
+ * To clear the status use {@link #setStatus(CharSequence) <code>setStatus(null)</code>}
+ *
+ * @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
* may set as a tag the key of the print job returned by a remote
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 62d214e..8f73518 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -329,7 +329,7 @@
final int printJobInfoCount = printJobInfos.size();
printJobs = new ArrayList<PrintJob>(printJobInfoCount);
for (int i = 0; i < printJobInfoCount; i++) {
- printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
+ printJobs.add(new PrintJob(this, printJobInfos.get(i), mClient));
}
}
if (printJobs != null) {
@@ -549,7 +549,7 @@
+ 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 @@
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 53cb56e..c46851e 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -35,6 +35,20 @@
}
/**
+ * 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.
*