am c7c4d6dd: Merge "Refinement of the print service APIs." into klp-dev

* commit 'c7c4d6dd4df2b177d0b6e382fdcde9bf2032baea':
  Refinement of the print service APIs.
This commit is contained in:
Svetoslav Ganov
2013-08-11 14:47:26 -07:00
committed by Android Git Automerger
28 changed files with 2060 additions and 1404 deletions

View File

@@ -162,8 +162,9 @@ LOCAL_SRC_FILES += \
core/java/android/os/IVibratorService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoverySessionController.aidl \
core/java/android/print/IPrinterDiscoverySessionObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintClient.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \

View File

@@ -167,6 +167,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew
$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/libandroidfw_intermediates/import_includes)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrinterDiscoveryObserver.*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************

View File

@@ -18629,11 +18629,11 @@ package android.print {
}
public static final class PrintAttributes.MediaSize {
ctor public PrintAttributes.MediaSize(java.lang.String, java.lang.CharSequence, int, int);
ctor public PrintAttributes.MediaSize(java.lang.String, java.lang.String, int, int);
method public static android.print.PrintAttributes.MediaSize createMediaSize(android.content.pm.PackageManager, int);
method public int getHeightMils();
method public java.lang.String getId();
method public java.lang.CharSequence getLabel();
method public java.lang.String getLabel();
method public int getWidthMils();
field public static final int ISO_A0 = 1; // 0x1
field public static final int ISO_A1 = 2; // 0x2
@@ -18677,17 +18677,17 @@ package android.print {
}
public static final class PrintAttributes.Resolution {
ctor public PrintAttributes.Resolution(java.lang.String, java.lang.CharSequence, int, int);
ctor public PrintAttributes.Resolution(java.lang.String, java.lang.String, int, int);
method public int getHorizontalDpi();
method public java.lang.String getId();
method public java.lang.CharSequence getLabel();
method public java.lang.String getLabel();
method public int getVerticalDpi();
}
public static final class PrintAttributes.Tray {
ctor public PrintAttributes.Tray(java.lang.String, java.lang.CharSequence);
ctor public PrintAttributes.Tray(java.lang.String, java.lang.String);
method public java.lang.String getId();
method public java.lang.CharSequence getLabel();
method public java.lang.String getLabel();
}
public abstract class PrintDocumentAdapter {
@@ -18714,6 +18714,7 @@ package android.print {
public final class PrintDocumentInfo implements android.os.Parcelable {
method public int describeContents();
method public int getContentType();
method public java.lang.String getName();
method public int getPageCount();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_DOCUMENT = 0; // 0x0
@@ -18724,12 +18725,18 @@ package android.print {
}
public static final class PrintDocumentInfo.Builder {
ctor public PrintDocumentInfo.Builder();
ctor public PrintDocumentInfo.Builder(java.lang.String);
method public android.print.PrintDocumentInfo create();
method public android.print.PrintDocumentInfo.Builder setContentType(int);
method public android.print.PrintDocumentInfo.Builder setPageCount(int);
}
public final class PrintFileDocumentAdapter extends android.print.PrintDocumentAdapter {
ctor public PrintFileDocumentAdapter(android.content.Context, java.io.File, android.print.PrintDocumentInfo);
method public void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
method public void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
}
public final class PrintJob {
method public void cancel();
method public int getId();
@@ -18758,50 +18765,66 @@ package android.print {
public final class PrintManager {
method public java.util.List<android.print.PrintJob> getPrintJobs();
method public android.print.PrintJob print(java.lang.String, java.io.File, android.print.PrintAttributes);
method public android.print.PrintJob print(java.lang.String, java.io.File, android.print.PrintDocumentInfo, android.print.PrintAttributes);
method public android.print.PrintJob print(java.lang.String, android.print.PrintDocumentAdapter, android.print.PrintAttributes);
}
public final class PrinterId implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getPrinterName();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public final class PrinterInfo implements android.os.Parcelable {
public final class PrinterCapabilitiesInfo implements android.os.Parcelable {
method public int describeContents();
method public int getColorModes();
method public void getDefaults(android.print.PrintAttributes);
method public int getDuplexModes();
method public int getFittingModes();
method public android.print.PrinterId getId();
method public java.util.List<android.print.PrintAttributes.Tray> getInputTrays();
method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes();
method public android.print.PrintAttributes.Margins getMinMargins();
method public int getOrientations();
method public java.util.List<android.print.PrintAttributes.Tray> getOutputTrays();
method public java.util.List<android.print.PrintAttributes.Resolution> getResolutions();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public static final class PrinterCapabilitiesInfo.Builder {
ctor public PrinterCapabilitiesInfo.Builder(android.print.PrinterId);
method public android.print.PrinterCapabilitiesInfo.Builder addInputTray(android.print.PrintAttributes.Tray, boolean);
method public android.print.PrinterCapabilitiesInfo.Builder addMediaSize(android.print.PrintAttributes.MediaSize, boolean);
method public android.print.PrinterCapabilitiesInfo.Builder addOutputTray(android.print.PrintAttributes.Tray, boolean);
method public android.print.PrinterCapabilitiesInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
method public android.print.PrinterCapabilitiesInfo create();
method public android.print.PrinterCapabilitiesInfo.Builder setColorModes(int, int);
method public android.print.PrinterCapabilitiesInfo.Builder setDuplexModes(int, int);
method public android.print.PrinterCapabilitiesInfo.Builder setFittingModes(int, int);
method public android.print.PrinterCapabilitiesInfo.Builder setMinMargins(android.print.PrintAttributes.Margins, android.print.PrintAttributes.Margins);
method public android.print.PrinterCapabilitiesInfo.Builder setOrientations(int, int);
}
public final class PrinterId implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getLocalId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public final class PrinterInfo implements android.os.Parcelable {
method public int describeContents();
method public android.print.PrinterCapabilitiesInfo getCapabilities();
method public java.lang.String getDescription();
method public android.print.PrinterId getId();
method public java.lang.String getName();
method public int getStatus();
method public boolean hasAllRequiredAttributes();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int STATUS_READY = 1; // 0x1
}
public static final class PrinterInfo.Builder {
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);
method public android.print.PrinterInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
ctor public PrinterInfo.Builder(android.print.PrinterId, java.lang.String, int);
ctor public PrinterInfo.Builder(android.print.PrinterInfo);
method public android.print.PrinterInfo create();
method public android.print.PrinterInfo.Builder setColorModes(int, int);
method public android.print.PrinterInfo.Builder setDuplexModes(int, int);
method public android.print.PrinterInfo.Builder setFittingModes(int, int);
method public android.print.PrinterInfo.Builder setMinMargins(android.print.PrintAttributes.Margins, android.print.PrintAttributes.Margins);
method public android.print.PrinterInfo.Builder setOrientations(int, int);
method public android.print.PrinterInfo.Builder setStatus(int);
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 setName(java.lang.String);
}
}
@@ -18853,6 +18876,9 @@ package android.printservice {
method public android.printservice.PrintDocument getDocument();
method public int getId();
method public android.print.PrintJobInfo getInfo();
method public boolean isCancelled();
method public boolean isCompleted();
method public boolean isFailed();
method public boolean isQueued();
method public boolean isStarted();
method public boolean setTag(java.lang.String);
@@ -18861,23 +18887,30 @@ package android.printservice {
public abstract class PrintService extends android.app.Service {
ctor public PrintService();
method public final void addDiscoveredPrinters(java.util.List<android.print.PrinterInfo>);
method protected final void attachBaseContext(android.content.Context);
method public final android.print.PrinterId generatePrinterId(java.lang.String);
method public final java.util.List<android.printservice.PrintJob> getPrintJobs();
method public final java.util.List<android.printservice.PrintJob> getActivePrintJobs();
method public final android.os.IBinder onBind(android.content.Intent);
method protected void onConnected();
method protected abstract android.printservice.PrinterDiscoverySession onCreatePrinterDiscoverySession();
method protected void onDisconnected();
method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
method protected void onRequestCancelPrintJob(android.printservice.PrintJob);
method protected void onRequestUpdatePrinters(java.util.List<android.print.PrinterId>);
method protected abstract void onStartPrinterDiscovery();
method protected abstract void onStopPrinterDiscovery();
method public final void removeDiscoveredPrinters(java.util.List<android.print.PrinterId>);
method public final void updateDiscoveredPrinters(java.util.List<android.print.PrinterInfo>);
method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
}
public abstract class PrinterDiscoverySession {
ctor public PrinterDiscoverySession(android.content.Context);
method public final void addPrinters(java.util.List<android.print.PrinterInfo>);
method public final boolean isClosed();
method public abstract void onClose();
method public abstract void onOpen(java.util.List<android.print.PrinterId>);
method public abstract void onRequestPrinterUpdate(android.print.PrinterId);
method public final void removePrinters(java.util.List<android.print.PrinterId>);
method public final void updatePrinters(java.util.List<android.print.PrinterInfo>);
}
}
package android.provider {

View File

@@ -17,7 +17,7 @@
package android.print;
import android.content.ComponentName;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrinterId;
import android.print.PrintJobInfo;
@@ -28,10 +28,8 @@ import android.print.PrintJobInfo;
* @hide
*/
oneway interface IPrintSpoolerClient {
void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
void onPrintJobQueued(in PrintJobInfo printJob);
void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
void onStopPrinterDiscovery();
void onRequestUpdatePrinters(in List<PrinterId> printerIds);
void onAllPrintJobsForServiceHandled(in ComponentName printService);
void onAllPrintJobsHandled();
}

View File

@@ -17,15 +17,14 @@
package android.print;
import android.print.PrinterId;
import android.print.PrinterInfo;
/**
* Interface for observing printer discovery.
* Interface for the controlling part of a printer discovery session.
*
* @hide
*/
oneway interface IPrinterDiscoveryObserver {
void onPrintersAdded(in List<PrinterInfo> printers);
void onPrintersRemoved(in List<PrinterId> printers);
void onPrintersUpdated(in List<PrinterInfo> printers);
oneway interface IPrinterDiscoverySessionController {
void open(in List<PrinterId> priorityList);
void requestPrinterUpdate(in PrinterId printerId);
void close();
}

View File

@@ -0,0 +1,33 @@
/*
* 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.IPrinterDiscoverySessionController;
import android.print.PrinterId;
import android.print.PrinterInfo;
/**
* Interface for the observing part of a printer discovery session.
*
* @hide
*/
oneway interface IPrinterDiscoverySessionObserver {
void setController(IPrinterDiscoverySessionController controller);
void onPrintersAdded(in List<PrinterInfo> printers);
void onPrintersRemoved(in List<PrinterId> printerIds);
void onPrintersUpdated(in List<PrinterInfo> printerIds);
}

View File

@@ -931,7 +931,7 @@ public final class PrintAttributes implements Parcelable {
}
private final String mId;
private final CharSequence mLabel;
private final String mLabel;
private final int mWidthMils;
private final int mHeightMils;
@@ -948,7 +948,7 @@ public final class PrintAttributes implements Parcelable {
* @throws IllegalArgumentException If the widthMils is less than or equal to zero.
* @throws IllegalArgumentException If the heightMils is less than or equal to zero.
*/
public MediaSize(String id, CharSequence label, int widthMils, int heightMils) {
public MediaSize(String id, String label, int widthMils, int heightMils) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("id cannot be empty.");
}
@@ -983,7 +983,7 @@ public final class PrintAttributes implements Parcelable {
*
* @return The human readable label.
*/
public CharSequence getLabel() {
public String getLabel() {
return mLabel;
}
@@ -1007,7 +1007,7 @@ public final class PrintAttributes implements Parcelable {
void writeToParcel(Parcel parcel) {
parcel.writeString(mId);
parcel.writeCharSequence(mLabel);
parcel.writeString(mLabel);
parcel.writeInt(mWidthMils);
parcel.writeInt(mHeightMils);
}
@@ -1015,7 +1015,7 @@ public final class PrintAttributes implements Parcelable {
static MediaSize createFromParcel(Parcel parcel) {
return new MediaSize(
parcel.readString(),
parcel.readCharSequence(),
parcel.readString(),
parcel.readInt(),
parcel.readInt());
}
@@ -1076,7 +1076,7 @@ public final class PrintAttributes implements Parcelable {
*/
public static final class Resolution {
private final String mId;
private final CharSequence mLabel;
private final String mLabel;
private final int mHorizontalDpi;
private final int mVerticalDpi;
@@ -1093,7 +1093,7 @@ public final class PrintAttributes implements Parcelable {
* @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero.
* @throws IllegalArgumentException If the verticalDpi is less than or equal to zero.
*/
public Resolution(String id, CharSequence label, int horizontalDpi, int verticalDpi) {
public Resolution(String id, String label, int horizontalDpi, int verticalDpi) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("id cannot be empty.");
}
@@ -1128,7 +1128,7 @@ public final class PrintAttributes implements Parcelable {
*
* @return The human readable label.
*/
public CharSequence getLabel() {
public String getLabel() {
return mLabel;
}
@@ -1152,7 +1152,7 @@ public final class PrintAttributes implements Parcelable {
void writeToParcel(Parcel parcel) {
parcel.writeString(mId);
parcel.writeCharSequence(mLabel);
parcel.writeString(mLabel);
parcel.writeInt(mHorizontalDpi);
parcel.writeInt(mVerticalDpi);
}
@@ -1160,7 +1160,7 @@ public final class PrintAttributes implements Parcelable {
static Resolution createFromParcel(Parcel parcel) {
return new Resolution(
parcel.readString(),
parcel.readCharSequence(),
parcel.readString(),
parcel.readInt(),
parcel.readInt());
}
@@ -1364,7 +1364,7 @@ public final class PrintAttributes implements Parcelable {
*/
public static final class Tray {
private final String mId;
private final CharSequence mLabel;
private final String mLabel;
/**
* Creates a new instance.
@@ -1375,7 +1375,7 @@ public final class PrintAttributes implements Parcelable {
* @throws IllegalArgumentException If the id is empty.
* @throws IllegalArgumentException If the label is empty.
*/
public Tray(String id, CharSequence label) {
public Tray(String id, String label) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("id cannot be empty.");
}
@@ -1400,19 +1400,19 @@ public final class PrintAttributes implements Parcelable {
*
* @return The human readable label.
*/
public CharSequence getLabel() {
public String getLabel() {
return mLabel;
}
void writeToParcel(Parcel parcel) {
parcel.writeString(mId);
parcel.writeCharSequence(mLabel);
parcel.writeString(mLabel);
}
static Tray createFromParcel(Parcel parcel) {
return new Tray(
parcel.readString(),
parcel.readCharSequence());
parcel.readString());
}
@Override
@@ -1457,7 +1457,7 @@ public final class PrintAttributes implements Parcelable {
}
}
private static String duplexModeToString(int duplexMode) {
static String duplexModeToString(int duplexMode) {
switch (duplexMode) {
case DUPLEX_MODE_NONE: {
return "DUPLEX_MODE_NONE";
@@ -1473,7 +1473,7 @@ public final class PrintAttributes implements Parcelable {
}
}
private static String colorModeToString(int colorMode) {
static String colorModeToString(int colorMode) {
switch (colorMode) {
case COLOR_MODE_MONOCHROME: {
return "COLOR_MODE_MONOCHROME";
@@ -1486,7 +1486,7 @@ public final class PrintAttributes implements Parcelable {
}
}
private static String orientationToString(int orientation) {
static String orientationToString(int orientation) {
switch (orientation) {
case ORIENTATION_PORTRAIT: {
return "ORIENTATION_PORTRAIT";
@@ -1499,7 +1499,7 @@ public final class PrintAttributes implements Parcelable {
}
}
private static String fittingModeToString(int fittingMode) {
static String fittingModeToString(int fittingMode) {
switch (fittingMode) {
case FITTING_MODE_NONE: {
return "FITTING_MODE_NONE";

View File

@@ -18,6 +18,7 @@ package android.print;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
/**
* This class encapsulates information about a printed document.
@@ -44,6 +45,7 @@ public final class PrintDocumentInfo implements Parcelable {
*/
public static final int CONTENT_TYPE_PHOTO = 1;
private String mName;
private int mPageCount;
private int mContentType;
@@ -61,6 +63,7 @@ public final class PrintDocumentInfo implements Parcelable {
* @param Prototype from which to clone.
*/
private PrintDocumentInfo(PrintDocumentInfo prototype) {
mName = prototype.mName;
mPageCount = prototype.mPageCount;
mContentType = prototype.mContentType;
}
@@ -71,10 +74,20 @@ public final class PrintDocumentInfo implements Parcelable {
* @param parcel Data from which to initialize.
*/
private PrintDocumentInfo(Parcel parcel) {
mName = parcel.readString();
mPageCount = parcel.readInt();
mContentType = parcel.readInt();
}
/**
* Gets the document name.
*
* @return The document name.
*/
public String getName() {
return mName;
}
/**
* Gets the total number of pages.
*
@@ -106,6 +119,7 @@ public final class PrintDocumentInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mName);
parcel.writeInt(mPageCount);
parcel.writeInt(mContentType);
}
@@ -114,6 +128,7 @@ public final class PrintDocumentInfo implements Parcelable {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mName != null) ? mName.hashCode() : 0);
result = prime * result + mContentType;
result = prime * result + mPageCount;
return result;
@@ -131,6 +146,9 @@ public final class PrintDocumentInfo implements Parcelable {
return false;
}
PrintDocumentInfo other = (PrintDocumentInfo) obj;
if (!TextUtils.equals(mName, other.mName)) {
return false;
}
if (mContentType != other.mContentType) {
return false;
}
@@ -144,17 +162,47 @@ public final class PrintDocumentInfo implements Parcelable {
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PrintDocumentInfo{");
builder.append("pageCount: ").append(mPageCount);
builder.append(", contentType: ").append(mContentType);
builder.append("name=").append(mName);
builder.append(", pageCount=").append(mPageCount);
builder.append(", contentType=").append(contentTyepToString(mContentType));
builder.append("}");
return builder.toString();
}
private String contentTyepToString(int contentType) {
switch (contentType) {
case CONTENT_TYPE_DOCUMENT: {
return "CONTENT_TYPE_DOCUMENT";
}
case CONTENT_TYPE_PHOTO: {
return "CONTENT_TYPE_PHOTO";
}
default: {
return "CONTENT_TYPE_UNKNOWN";
}
}
}
/**
* Builder for creating an {@link PrintDocumentInfo}.
*/
public static final class Builder {
private final PrintDocumentInfo mPrototype = new PrintDocumentInfo();
private final PrintDocumentInfo mPrototype;
/**
* Constructor.
*
* @param name The document name. Cannot be empty.
*
* @throws IllegalArgumentException If the name is empty.
*/
public Builder(String name) {
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("name cannot be empty");
}
mPrototype = new PrintDocumentInfo();
mPrototype.mName = name;
}
/**
* Sets the total number of pages.

View File

@@ -36,34 +36,48 @@ import java.io.InputStream;
import java.io.OutputStream;
/**
* Adapter for printing files.
* Adapter for printing files. This class could be useful if you
* want to print a file and intercept when the system is ready
* spooling the data, so you can deleted the file if it is a
* temporary one.
*/
final class FileDocumentAdapter extends PrintDocumentAdapter {
public final class PrintFileDocumentAdapter extends PrintDocumentAdapter {
private static final String LOG_TAG = "FileDocumentAdapter";
private static final String LOG_TAG = "PrintedFileDocumentAdapter";
private final Context mContext;
private final File mFile;
private final PrintDocumentInfo mDocumentInfo;
private WriteFileAsyncTask mWriteFileAsyncTask;
public FileDocumentAdapter(Context context, File file) {
/**
* Constructor.
*
* @param context Context for accessing resources.
* @param file The file to print.
* @param documentInfo The information about the printed file.
*/
public PrintFileDocumentAdapter(Context context, File file,
PrintDocumentInfo documentInfo) {
if (file == null) {
throw new IllegalArgumentException("File cannot be null!");
}
if (documentInfo == null) {
throw new IllegalArgumentException("documentInfo cannot be null!");
}
mContext = context;
mFile = file;
mDocumentInfo = documentInfo;
}
@Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
CancellationSignal cancellationSignal, LayoutResultCallback callback,
Bundle metadata) {
// TODO: When we have a PDF rendering library we should query the page count.
PrintDocumentInfo info = new PrintDocumentInfo.Builder()
.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).create();
callback.onLayoutFinished(info, false);
callback.onLayoutFinished(mDocumentInfo, false);
}
@Override

View File

@@ -109,6 +109,9 @@ public final class PrintJobInfo implements Parcelable {
/** The unique id of the printer. */
private PrinterId mPrinterId;
/** The name of the printer - internally used */
private String mPrinterName;
/** The status of the print job. */
private int mState;
@@ -146,6 +149,7 @@ public final class PrintJobInfo implements Parcelable {
mId = other.mId;
mLabel = other.mLabel;
mPrinterId = other.mPrinterId;
mPrinterName = other.mPrinterName;
mState = other.mState;
mAppId = other.mAppId;
mUserId = other.mUserId;
@@ -161,6 +165,7 @@ public final class PrintJobInfo implements Parcelable {
mId = parcel.readInt();
mLabel = parcel.readCharSequence();
mPrinterId = parcel.readParcelable(null);
mPrinterName = parcel.readString();
mState = parcel.readInt();
mAppId = parcel.readInt();
mUserId = parcel.readInt();
@@ -244,6 +249,28 @@ public final class PrintJobInfo implements Parcelable {
mPrinterId = printerId;
}
/**
* Gets the name of the target printer.
*
* @return The printer name.
*
* @hide
*/
public String getPrinterName() {
return mPrinterName;
}
/**
* Sets the name of the target printer.
*
* @param printerName The printer name.
*
* @hide
*/
public void setPrinterName(String printerName) {
mPrinterName = printerName;
}
/**
* Gets the current job state.
*
@@ -445,6 +472,7 @@ public final class PrintJobInfo implements Parcelable {
parcel.writeInt(mId);
parcel.writeCharSequence(mLabel);
parcel.writeParcelable(mPrinterId, flags);
parcel.writeString(mPrinterName);
parcel.writeInt(mState);
parcel.writeInt(mAppId);
parcel.writeInt(mUserId);

View File

@@ -161,13 +161,16 @@ public final class PrintManager {
*
* @param printJobName A name for the new print job.
* @param pdfFile The PDF file to print.
* @param documentInfo Information about the printed document.
* @param attributes The default print job attributes.
* @return The created print job.
*
* @see PrintJob
*/
public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
FileDocumentAdapter documentAdapter = new FileDocumentAdapter(mContext, pdfFile);
public PrintJob print(String printJobName, File pdfFile, PrintDocumentInfo documentInfo,
PrintAttributes attributes) {
PrintFileDocumentAdapter documentAdapter = new PrintFileDocumentAdapter(
mContext, pdfFile, documentInfo);
return print(printJobName, documentAdapter, attributes);
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2013, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.print;
parcelable PrinterCapabilitiesInfo;

View File

@@ -0,0 +1,972 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintAttributes.Tray;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This class represents the capabilities of a printer.
*/
public final class PrinterCapabilitiesInfo implements Parcelable {
/**
* Undefined default value.
*
* @hide
*/
public static final int DEFAULT_UNDEFINED = -1;
private static final int PROPERTY_MEDIA_SIZE = 0;
private static final int PROPERTY_RESOLUTION = 1;
private static final int PROPERTY_INPUT_TRAY = 2;
private static final int PROPERTY_OUTPUT_TRAY = 3;
private static final int PROPERTY_DUPLEX_MODE = 4;
private static final int PROPERTY_COLOR_MODE = 5;
private static final int PROPERTY_FITTING_MODE = 6;
private static final int PROPERTY_ORIENTATION = 7;
private static final int PROPERTY_COUNT = 8;
private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0);
private Margins mMinMargins = DEFAULT_MARGINS;
private List<MediaSize> mMediaSizes;
private List<Resolution> mResolutions;
private List<Tray> mInputTrays;
private List<Tray> mOutputTrays;
private int mDuplexModes;
private int mColorModes;
private int mFittingModes;
private int mOrientations;
private final int[] mDefaults = new int[PROPERTY_COUNT];
private Margins mDefaultMargins = DEFAULT_MARGINS;
/**
* @hide
*/
public PrinterCapabilitiesInfo() {
Arrays.fill(mDefaults, DEFAULT_UNDEFINED);
}
/**
* @hide
*/
public PrinterCapabilitiesInfo(PrinterCapabilitiesInfo prototype) {
copyFrom(prototype);
}
/**
* @hide
*/
public void copyFrom(PrinterCapabilitiesInfo other) {
mMinMargins = other.mMinMargins;
if (other.mMediaSizes != null) {
if (mMediaSizes != null) {
mMediaSizes.clear();
mMediaSizes.addAll(other.mMediaSizes);
} else {
mMediaSizes = new ArrayList<MediaSize>(other.mMediaSizes);
}
} else {
mMediaSizes = null;
}
if (other.mResolutions != null) {
if (mResolutions != null) {
mResolutions.clear();
mResolutions.addAll(other.mResolutions);
} else {
mResolutions = new ArrayList<Resolution>(other.mResolutions);
}
} else {
mResolutions = null;
}
if (other.mInputTrays != null) {
if (mInputTrays != null) {
mInputTrays.clear();
mInputTrays.addAll(other.mInputTrays);
} else {
mInputTrays = new ArrayList<Tray>(other.mInputTrays);
}
} else {
mInputTrays = null;
}
if (other.mOutputTrays != null) {
if (mOutputTrays != null) {
mOutputTrays.clear();
mOutputTrays.addAll(other.mOutputTrays);
} else {
mOutputTrays = new ArrayList<Tray>(other.mOutputTrays);
}
} else {
mOutputTrays = null;
}
mDuplexModes = other.mDuplexModes;
mColorModes = other.mColorModes;
mFittingModes = other.mFittingModes;
mOrientations = other.mOrientations;
final int defaultCount = other.mDefaults.length;
for (int i = 0; i < defaultCount; i++) {
mDefaults[i] = other.mDefaults[i];
}
mDefaultMargins = other.mDefaultMargins;
}
/**
* Gets the supported media sizes.
*
* @return The media sizes.
*/
public List<MediaSize> getMediaSizes() {
return mMediaSizes;
}
/**
* Gets the supported resolutions.
*
* @return The resolutions.
*/
public List<Resolution> getResolutions() {
return mResolutions;
}
/**
* Gets the minimal supported margins.
*
* @return The minimal margins.
*/
public Margins getMinMargins() {
return mMinMargins;
}
/**
* Gets the available input trays.
*
* @return The input trays.
*/
public List<Tray> getInputTrays() {
return mInputTrays;
}
/**
* Gets the available output trays.
*
* @return The output trays.
*/
public List<Tray> getOutputTrays() {
return mOutputTrays;
}
/**
* Gets the supported duplex modes.
*
* @return The duplex modes.
*
* @see PrintAttributes#DUPLEX_MODE_NONE
* @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
* @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
*/
public int getDuplexModes() {
return mDuplexModes;
}
/**
* Gets the supported color modes.
*
* @return The color modes.
*
* @see PrintAttributes#COLOR_MODE_COLOR
* @see PrintAttributes#COLOR_MODE_MONOCHROME
*/
public int getColorModes() {
return mColorModes;
}
/**
* Gets the supported fitting modes.
*
* @return The fitting modes.
*
* @see PrintAttributes#FITTING_MODE_NONE
* @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
*/
public int getFittingModes() {
return mFittingModes;
}
/**
* Gets the supported orientations.
*
* @return The orientations.
*
* @see PrintAttributes#ORIENTATION_PORTRAIT
* @see PrintAttributes#ORIENTATION_LANDSCAPE
*/
public int getOrientations() {
return mOrientations;
}
/**
* Gets the default print attributes.
*
* @param outAttributes The attributes to populated.
*/
public void getDefaults(PrintAttributes outAttributes) {
outAttributes.clear();
outAttributes.setMargins(mDefaultMargins);
final int mediaSizeIndex = mDefaults[PROPERTY_MEDIA_SIZE];
if (mediaSizeIndex >= 0) {
outAttributes.setMediaSize(mMediaSizes.get(mediaSizeIndex));
}
final int resolutionIndex = mDefaults[PROPERTY_RESOLUTION];
if (resolutionIndex >= 0) {
outAttributes.setResolution(mResolutions.get(resolutionIndex));
}
final int inputTrayIndex = mDefaults[PROPERTY_INPUT_TRAY];
if (inputTrayIndex >= 0) {
outAttributes.setInputTray(mInputTrays.get(inputTrayIndex));
}
final int outputTrayIndex = mDefaults[PROPERTY_OUTPUT_TRAY];
if (outputTrayIndex >= 0) {
outAttributes.setOutputTray(mOutputTrays.get(outputTrayIndex));
}
final int duplexMode = mDefaults[PROPERTY_DUPLEX_MODE];
if (duplexMode > 0) {
outAttributes.setDuplexMode(duplexMode);
}
final int colorMode = mDefaults[PROPERTY_COLOR_MODE];
if (colorMode > 0) {
outAttributes.setColorMode(colorMode);
}
final int fittingMode = mDefaults[PROPERTY_FITTING_MODE];
if (fittingMode > 0) {
outAttributes.setFittingMode(fittingMode);
}
final int orientation = mDefaults[PROPERTY_ORIENTATION];
if (orientation > 0) {
outAttributes.setOrientation(orientation);
}
}
private PrinterCapabilitiesInfo(Parcel parcel) {
mMinMargins = readMargins(parcel);
readMediaSizes(parcel);
readResolutions(parcel);
mInputTrays = readInputTrays(parcel);
mOutputTrays = readOutputTrays(parcel);
mColorModes = parcel.readInt();
mDuplexModes = parcel.readInt();
mFittingModes = parcel.readInt();
mOrientations = parcel.readInt();
readDefaults(parcel);
mDefaultMargins = readMargins(parcel);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
writeMargins(mMinMargins, parcel);
writeMediaSizes(parcel);
writeResolutions(parcel);
writeInputTrays(parcel);
writeOutputTrays(parcel);
parcel.writeInt(mColorModes);
parcel.writeInt(mDuplexModes);
parcel.writeInt(mFittingModes);
parcel.writeInt(mOrientations);
writeDefaults(parcel);
writeMargins(mDefaultMargins, parcel);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode());
result = prime * result + ((mResolutions == null) ? 0 : mResolutions.hashCode());
result = prime * result + ((mInputTrays == null) ? 0 : mInputTrays.hashCode());
result = prime * result + ((mOutputTrays == null) ? 0 : mOutputTrays.hashCode());
result = prime * result + mColorModes;
result = prime * result + mDuplexModes;
result = prime * result + mFittingModes;
result = prime * result + mOrientations;
result = prime * result + Arrays.hashCode(mDefaults);
result = prime * result + ((mDefaultMargins == null) ? 0 : mDefaultMargins.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PrinterCapabilitiesInfo other = (PrinterCapabilitiesInfo) obj;
if (mMinMargins == null) {
if (other.mMinMargins != null) {
return false;
}
} else if (!mMinMargins.equals(other.mMinMargins)) {
return false;
}
if (mMediaSizes == null) {
if (other.mMediaSizes != null) {
return false;
}
} else if (!mMediaSizes.equals(other.mMediaSizes)) {
return false;
}
if (mResolutions == null) {
if (other.mResolutions != null) {
return false;
}
} else if (!mResolutions.equals(other.mResolutions)) {
return false;
}
if (mInputTrays == null) {
if (other.mInputTrays != null) {
return false;
}
} else if (!mInputTrays.equals(other.mInputTrays)) {
return false;
}
if (mOutputTrays == null) {
if (other.mOutputTrays != null) {
return false;
}
} else if (!mOutputTrays.equals(other.mOutputTrays)) {
return false;
}
if (mDuplexModes != other.mDuplexModes) {
return false;
}
if (mColorModes != other.mColorModes) {
return false;
}
if (mFittingModes != other.mFittingModes) {
return false;
}
if (mOrientations != other.mOrientations) {
return false;
}
if (!Arrays.equals(mDefaults, other.mDefaults)) {
return false;
}
if (mDefaultMargins == null) {
if (other.mDefaultMargins != null) {
return false;
}
} else if (!mDefaultMargins.equals(other.mDefaultMargins)) {
return false;
}
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PrinterInfo{");
builder.append("minMargins=").append(mMinMargins);
builder.append(", mediaSizes=").append(mMediaSizes);
builder.append(", resolutions=").append(mResolutions);
builder.append(", inputTrays=").append(mInputTrays);
builder.append(", outputTrays=").append(mOutputTrays);
builder.append(", duplexModes=").append(duplexModesToString());
builder.append(", colorModes=").append(colorModesToString());
builder.append(", fittingModes=").append(fittingModesToString());
builder.append(", orientations=").append(orientationsToString());
builder.append("\"}");
return builder.toString();
}
private String duplexModesToString() {
StringBuilder builder = new StringBuilder();
builder.append('[');
int duplexModes = mDuplexModes;
while (duplexModes != 0) {
final int duplexMode = 1 << Integer.numberOfTrailingZeros(duplexModes);
duplexModes &= ~duplexMode;
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(PrintAttributes.duplexModeToString(duplexMode));
}
builder.append(']');
return builder.toString();
}
private String colorModesToString() {
StringBuilder builder = new StringBuilder();
builder.append('[');
int colorModes = mColorModes;
while (colorModes != 0) {
final int colorMode = 1 << Integer.numberOfTrailingZeros(colorModes);
colorModes &= ~colorMode;
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(PrintAttributes.colorModeToString(colorMode));
}
builder.append(']');
return builder.toString();
}
private String fittingModesToString() {
StringBuilder builder = new StringBuilder();
builder.append('[');
int fittingModes = mFittingModes;
while (fittingModes != 0) {
final int fittingMode = 1 << Integer.numberOfTrailingZeros(fittingModes);
fittingModes &= ~fittingMode;
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(PrintAttributes.fittingModeToString(fittingMode));
}
builder.append(']');
return builder.toString();
}
private String orientationsToString() {
StringBuilder builder = new StringBuilder();
builder.append('[');
int orientations = mOrientations;
while (orientations != 0) {
final int orientation = 1 << Integer.numberOfTrailingZeros(orientations);
orientations &= ~orientation;
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(PrintAttributes.orientationToString(orientation));
}
builder.append(']');
return builder.toString();
}
private void writeMediaSizes(Parcel parcel) {
if (mMediaSizes == null) {
parcel.writeInt(0);
return;
}
final int mediaSizeCount = mMediaSizes.size();
parcel.writeInt(mediaSizeCount);
for (int i = 0; i < mediaSizeCount; i++) {
mMediaSizes.get(i).writeToParcel(parcel);
}
}
private void readMediaSizes(Parcel parcel) {
final int mediaSizeCount = parcel.readInt();
if (mediaSizeCount > 0 && mMediaSizes == null) {
mMediaSizes = new ArrayList<MediaSize>();
}
for (int i = 0; i < mediaSizeCount; i++) {
mMediaSizes.add(MediaSize.createFromParcel(parcel));
}
}
private void writeResolutions(Parcel parcel) {
if (mResolutions == null) {
parcel.writeInt(0);
return;
}
final int resolutionCount = mResolutions.size();
parcel.writeInt(resolutionCount);
for (int i = 0; i < resolutionCount; i++) {
mResolutions.get(i).writeToParcel(parcel);
}
}
private void readResolutions(Parcel parcel) {
final int resolutionCount = parcel.readInt();
if (resolutionCount > 0 && mResolutions == null) {
mResolutions = new ArrayList<Resolution>();
}
for (int i = 0; i < resolutionCount; i++) {
mResolutions.add(Resolution.createFromParcel(parcel));
}
}
private void writeMargins(Margins margins, Parcel parcel) {
if (margins == null) {
parcel.writeInt(0);
} else {
parcel.writeInt(1);
margins.writeToParcel(parcel);
}
}
private Margins readMargins(Parcel parcel) {
return (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
}
private void writeInputTrays(Parcel parcel) {
if (mInputTrays == null) {
parcel.writeInt(0);
return;
}
final int inputTrayCount = mInputTrays.size();
parcel.writeInt(inputTrayCount);
for (int i = 0; i < inputTrayCount; i++) {
mInputTrays.get(i).writeToParcel(parcel);
}
}
private List<Tray> readInputTrays(Parcel parcel) {
final int inputTrayCount = parcel.readInt();
if (inputTrayCount <= 0) {
return null;
}
List<Tray> inputTrays = new ArrayList<Tray>(inputTrayCount);
for (int i = 0; i < inputTrayCount; i++) {
inputTrays.add(Tray.createFromParcel(parcel));
}
return inputTrays;
}
private void writeOutputTrays(Parcel parcel) {
if (mOutputTrays == null) {
parcel.writeInt(0);
return;
}
final int outputTrayCount = mOutputTrays.size();
parcel.writeInt(outputTrayCount);
for (int i = 0; i < outputTrayCount; i++) {
mOutputTrays.get(i).writeToParcel(parcel);
}
}
private List<Tray> readOutputTrays(Parcel parcel) {
final int outputTrayCount = parcel.readInt();
if (outputTrayCount <= 0) {
return null;
}
List<Tray> outputTrays = new ArrayList<Tray>(outputTrayCount);
for (int i = 0; i < outputTrayCount; i++) {
outputTrays.add(Tray.createFromParcel(parcel));
}
return outputTrays;
}
private void readDefaults(Parcel parcel) {
final int defaultCount = parcel.readInt();
for (int i = 0; i < defaultCount; i++) {
mDefaults[i] = parcel.readInt();
}
}
private void writeDefaults(Parcel parcel) {
final int defaultCount = mDefaults.length;
parcel.writeInt(defaultCount);
for (int i = 0; i < defaultCount; i++) {
parcel.writeInt(mDefaults[i]);
}
}
/**
* Builder for creating of a {@link PrinterInfo}. This class is responsible
* to enforce that all required attributes have at least one default value.
* In other words, this class creates only well-formed {@link PrinterInfo}s.
* <p>
* Look at the individual methods for a reference whether a property is
* required or if it is optional.
* </p>
*/
public static final class Builder {
private final PrinterCapabilitiesInfo mPrototype;
/**
* Creates a new instance.
*
* @param printerId The printer id. Cannot be null.
*
* @throws IllegalArgumentException If the printer id is null.
*/
public Builder(PrinterId printerId) {
if (printerId == null) {
throw new IllegalArgumentException("printerId cannot be null.");
}
mPrototype = new PrinterCapabilitiesInfo();
}
/**
* Adds a supported media size.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param mediaSize A media size.
* @param isDefault Whether this is the default.
* @return This builder.
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.MediaSize
*/
public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
if (mPrototype.mMediaSizes == null) {
mPrototype.mMediaSizes = new ArrayList<MediaSize>();
}
final int insertionIndex = mPrototype.mMediaSizes.size();
mPrototype.mMediaSizes.add(mediaSize);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
mPrototype.mDefaults[PROPERTY_MEDIA_SIZE] = insertionIndex;
}
return this;
}
/**
* Adds a supported resolution.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param resolution A resolution.
* @param isDefault Whether this is the default.
* @return This builder.
*
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.Resolution
*/
public Builder addResolution(Resolution resolution, boolean isDefault) {
if (mPrototype.mResolutions == null) {
mPrototype.mResolutions = new ArrayList<Resolution>();
}
final int insertionIndex = mPrototype.mResolutions.size();
mPrototype.mResolutions.add(resolution);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
mPrototype.mDefaults[PROPERTY_RESOLUTION] = insertionIndex;
}
return this;
}
/**
* Sets the minimal margins.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param margins The margins.
* @param defaultMargins The default margins.
* @return This builder.
*
* @see PrintAttributes.Margins
*/
public Builder setMinMargins(Margins margins, Margins defaultMargins) {
if (margins.getLeftMils() > defaultMargins.getLeftMils()
|| margins.getTopMils() > defaultMargins.getTopMils()
|| margins.getRightMils() < defaultMargins.getRightMils()
|| margins.getBottomMils() < defaultMargins.getBottomMils()) {
throw new IllegalArgumentException("Default margins"
+ " cannot be outside of the min margins.");
}
mPrototype.mMinMargins = margins;
mPrototype.mDefaultMargins = defaultMargins;
return this;
}
/**
* Adds an input tray.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param inputTray A tray.
* @param isDefault Whether this is the default.
* @return This builder.
*
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.Tray
*/
public Builder addInputTray(Tray inputTray, boolean isDefault) {
if (mPrototype.mInputTrays == null) {
mPrototype.mInputTrays = new ArrayList<Tray>();
}
final int insertionIndex = mPrototype.mInputTrays.size();
mPrototype.mInputTrays.add(inputTray);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_INPUT_TRAY);
mPrototype.mDefaults[PROPERTY_INPUT_TRAY] = insertionIndex;
}
return this;
}
/**
* Adds an output tray.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param outputTray A tray.
* @param isDefault Whether this is the default.
* @return This builder.
*
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.Tray
*/
public Builder addOutputTray(Tray outputTray, boolean isDefault) {
if (mPrototype.mOutputTrays == null) {
mPrototype.mOutputTrays = new ArrayList<Tray>();
}
final int insertionIndex = mPrototype.mOutputTrays.size();
mPrototype.mOutputTrays.add(outputTray);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_OUTPUT_TRAY);
mPrototype.mDefaults[PROPERTY_OUTPUT_TRAY] = insertionIndex;
}
return this;
}
/**
* Sets the color modes.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param colorModes The color mode bit mask.
* @param defaultColorMode The default color mode.
* @return This builder.
*
* @throws IllegalArgumentException If color modes contains an invalid
* mode bit or if the default color mode is invalid.
*
* @see PrintAttributes#COLOR_MODE_COLOR
* @see PrintAttributes#COLOR_MODE_MONOCHROME
*/
public Builder setColorModes(int colorModes, int defaultColorMode) {
int currentModes = colorModes;
while (currentModes > 0) {
final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
currentModes &= ~currentMode;
PrintAttributes.enforceValidColorMode(currentMode);
}
if ((colorModes & defaultColorMode) == 0) {
throw new IllegalArgumentException("Default color mode not in color modes.");
}
PrintAttributes.enforceValidColorMode(colorModes);
mPrototype.mColorModes = colorModes;
mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
return this;
}
/**
* Set the duplex modes.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param duplexModes The duplex mode bit mask.
* @param defaultDuplexMode The default duplex mode.
* @return This builder.
*
* @throws IllegalArgumentException If duplex modes contains an invalid
* mode bit or if the default duplex mode is invalid.
*
* @see PrintAttributes#DUPLEX_MODE_NONE
* @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
* @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
*/
public Builder setDuplexModes(int duplexModes, int defaultDuplexMode) {
int currentModes = duplexModes;
while (currentModes > 0) {
final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
currentModes &= ~currentMode;
PrintAttributes.enforceValidDuplexMode(currentMode);
}
if ((duplexModes & defaultDuplexMode) == 0) {
throw new IllegalArgumentException("Default duplex mode not in duplex modes.");
}
PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
mPrototype.mDuplexModes = duplexModes;
mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode;
return this;
}
/**
* Sets the fitting modes.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param fittingModes The fitting mode bit mask.
* @param defaultFittingMode The default fitting mode.
* @return This builder.
*
* @throws IllegalArgumentException If fitting modes contains an invalid
* mode bit or if the default fitting mode is invalid.
*
* @see PrintAttributes#FITTING_MODE_NONE
* @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
*/
public Builder setFittingModes(int fittingModes, int defaultFittingMode) {
int currentModes = fittingModes;
while (currentModes > 0) {
final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
currentModes &= ~currentMode;
PrintAttributes.enfoceValidFittingMode(currentMode);
}
if ((fittingModes & defaultFittingMode) == 0) {
throw new IllegalArgumentException("Default fitting mode not in fiting modes.");
}
PrintAttributes.enfoceValidFittingMode(defaultFittingMode);
mPrototype.mFittingModes = fittingModes;
mPrototype.mDefaults[PROPERTY_FITTING_MODE] = defaultFittingMode;
return this;
}
/**
* Sets the orientations.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param orientations The orientation bit mask.
* @param defaultOrientation The default orientation.
* @return This builder.
*
* @throws IllegalArgumentException If orientations contains an invalid
* mode bit or if the default orientation is invalid.
*
* @see PrintAttributes#ORIENTATION_PORTRAIT
* @see PrintAttributes#ORIENTATION_LANDSCAPE
*/
public Builder setOrientations(int orientations, int defaultOrientation) {
int currentOrientaions = orientations;
while (currentOrientaions > 0) {
final int currentOrnt = (1 << Integer.numberOfTrailingZeros(currentOrientaions));
currentOrientaions &= ~currentOrnt;
PrintAttributes.enforceValidOrientation(currentOrnt);
}
if ((orientations & defaultOrientation) == 0) {
throw new IllegalArgumentException("Default orientation not in orientations.");
}
PrintAttributes.enforceValidOrientation(defaultOrientation);
mPrototype.mOrientations = orientations;
mPrototype.mDefaults[PROPERTY_ORIENTATION] = defaultOrientation;
return this;
}
/**
* Crates a new {@link PrinterCapabilitiesInfo} enforcing that all
* required properties have need specified. See individual methods
* in this class for reference about required attributes.
*
* @return A new {@link PrinterCapabilitiesInfo}.
*
* @throws IllegalStateException If a required attribute was not specified.
*/
public PrinterCapabilitiesInfo create() {
if (mPrototype.mMediaSizes == null || mPrototype.mMediaSizes.isEmpty()) {
throw new IllegalStateException("No media size specified.");
}
if (mPrototype.mDefaults[PROPERTY_MEDIA_SIZE] == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default media size specified.");
}
if (mPrototype.mResolutions == null || mPrototype.mResolutions.isEmpty()) {
throw new IllegalStateException("No resolution specified.");
}
if (mPrototype.mDefaults[PROPERTY_RESOLUTION] == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default resolution specified.");
}
if (mPrototype.mColorModes == 0) {
throw new IllegalStateException("No color mode specified.");
}
if (mPrototype.mDefaults[PROPERTY_COLOR_MODE] == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default color mode specified.");
}
if (mPrototype.mOrientations == 0) {
throw new IllegalStateException("No oprientation specified.");
}
if (mPrototype.mDefaults[PROPERTY_ORIENTATION] == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default orientation specified.");
}
if (mPrototype.mMinMargins == null) {
mPrototype.mMinMargins = new Margins(0, 0, 0, 0);
}
if (mPrototype.mDefaultMargins == null) {
mPrototype.mDefaultMargins = mPrototype.mMinMargins;
}
return new PrinterCapabilitiesInfo(mPrototype);
}
private void throwIfDefaultAlreadySpecified(int propertyIndex) {
if (mPrototype.mDefaults[propertyIndex] != DEFAULT_UNDEFINED) {
throw new IllegalArgumentException("Default already specified.");
}
}
}
public static final Parcelable.Creator<PrinterCapabilitiesInfo> CREATOR =
new Parcelable.Creator<PrinterCapabilitiesInfo>() {
@Override
public PrinterCapabilitiesInfo createFromParcel(Parcel parcel) {
return new PrinterCapabilitiesInfo(parcel);
}
@Override
public PrinterCapabilitiesInfo[] newArray(int size) {
return new PrinterCapabilitiesInfo[size];
}
};
}

View File

@@ -28,24 +28,24 @@ public final class PrinterId implements Parcelable {
private final ComponentName mServiceName;
private final String mPrinterName;
private final String mLocalId;
/**
* Creates a new instance.
*
* @param serviceName The managing print service.
* @param printerName The unique name within the managing service.
* @param localId The locally unique id within the managing service.
*
* @hide
*/
public PrinterId(ComponentName serviceName, String printerName) {
public PrinterId(ComponentName serviceName, String localId) {
mServiceName = serviceName;
mPrinterName = printerName;
mLocalId = localId;
}
private PrinterId(Parcel parcel) {
mServiceName = parcel.readParcelable(null);
mPrinterName = parcel.readString();
mLocalId = parcel.readString();
}
/**
@@ -60,13 +60,13 @@ public final class PrinterId implements Parcelable {
}
/**
* Gets the name of this printer which is unique in the context
* Gets the id of this printer which is unique in the context
* of the print service that manages it.
*
* @return The printer name.
*/
public String getPrinterName() {
return mPrinterName;
public String getLocalId() {
return mLocalId;
}
@Override
@@ -77,7 +77,7 @@ public final class PrinterId implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mServiceName, flags);
parcel.writeString(mPrinterName);
parcel.writeString(mLocalId);
}
@Override
@@ -99,7 +99,7 @@ public final class PrinterId implements Parcelable {
} else if (!mServiceName.equals(other.mServiceName)) {
return false;
}
if (!TextUtils.equals(mPrinterName, other.mPrinterName)) {
if (!TextUtils.equals(mLocalId, other.mLocalId)) {
return false;
}
return true;
@@ -111,7 +111,7 @@ public final class PrinterId implements Parcelable {
int hashCode = 1;
hashCode = prime * hashCode + ((mServiceName != null)
? mServiceName.hashCode() : 1);
hashCode = prime * hashCode + mPrinterName.hashCode();
hashCode = prime * hashCode + mLocalId.hashCode();
return hashCode;
}
@@ -119,9 +119,8 @@ public final class PrinterId implements Parcelable {
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PrinterId{");
builder.append(mServiceName.flattenToString());
builder.append(":");
builder.append(mPrinterName);
builder.append("serviceName=").append(mServiceName.flattenToString());
builder.append(", localId=").append(mLocalId);
builder.append('}');
return builder.toString();
}

View File

@@ -18,67 +18,30 @@ package android.print;
import android.os.Parcel;
import android.os.Parcelable;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintAttributes.Tray;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.text.TextUtils;
/**
* This class represents the description of a printer. A description
* contains the printer id, human readable name, status, and available
* options for various printer capabilities, such as media size, etc.
* This class represents the description of a printer.
*/
public final class PrinterInfo implements Parcelable {
/**
* Undefined default value.
*
* @hide
*/
public static final int DEFAULT_UNDEFINED = -1;
private static final int PROPERTY_MEDIA_SIZE = 0;
private static final int PROPERTY_RESOLUTION = 1;
private static final int PROPERTY_INPUT_TRAY = 2;
private static final int PROPERTY_OUTPUT_TRAY = 3;
private static final int PROPERTY_DUPLEX_MODE = 4;
private static final int PROPERTY_COLOR_MODE = 5;
private static final int PROPERTY_FITTING_MODE = 6;
private static final int PROPERTY_ORIENTATION = 7;
private static final int PROPERTY_COUNT = 8;
/** Printer status: the printer is ready to print. */
public static final int STATUS_READY = 1;
private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0);
// TODO: Add printer status constants.
private PrinterId mId;
private String mName;
private int mStatus;
private Margins mMinMargins = DEFAULT_MARGINS;
private List<MediaSize> mMediaSizes;
private List<Resolution> mResolutions;
private List<Tray> mInputTrays;
private List<Tray> mOutputTrays;
private String mDescription;
private int mDuplexModes;
private int mColorModes;
private int mFittingModes;
private int mOrientations;
private PrinterCapabilitiesInfo mCapabilities;
private final int[] mDefaults = new int[PROPERTY_COUNT];
private Margins mDefaultMargins = DEFAULT_MARGINS;
/**
* @hide
*/
public PrinterInfo() {
Arrays.fill(mDefaults, DEFAULT_UNDEFINED);
private PrinterInfo() {
/* do nothing */
}
private PrinterInfo(PrinterInfo prototype) {
@@ -90,63 +53,18 @@ public final class PrinterInfo implements Parcelable {
*/
public void copyFrom(PrinterInfo other) {
mId = other.mId;
mName = other.mName;
mStatus = other.mStatus;
mMinMargins = other.mMinMargins;
if (other.mMediaSizes != null) {
if (mMediaSizes != null) {
mMediaSizes.clear();
mMediaSizes.addAll(other.mMediaSizes);
mDescription = other.mDescription;
if (other.mCapabilities != null) {
if (mCapabilities != null) {
mCapabilities.copyFrom(other.mCapabilities);
} else {
mMediaSizes = new ArrayList<MediaSize>(other.mMediaSizes);
mCapabilities = new PrinterCapabilitiesInfo(other.mCapabilities);
}
} else {
mMediaSizes = null;
mCapabilities = null;
}
if (other.mResolutions != null) {
if (mResolutions != null) {
mResolutions.clear();
mResolutions.addAll(other.mResolutions);
} else {
mResolutions = new ArrayList<Resolution>(other.mResolutions);
}
} else {
mResolutions = null;
}
if (other.mInputTrays != null) {
if (mInputTrays != null) {
mInputTrays.clear();
mInputTrays.addAll(other.mInputTrays);
} else {
mInputTrays = new ArrayList<Tray>(other.mInputTrays);
}
} else {
mInputTrays = null;
}
if (other.mOutputTrays != null) {
if (mOutputTrays != null) {
mOutputTrays.clear();
mOutputTrays.addAll(other.mOutputTrays);
} else {
mOutputTrays = new ArrayList<Tray>(other.mOutputTrays);
}
} else {
mOutputTrays = null;
}
mDuplexModes = other.mDuplexModes;
mColorModes = other.mColorModes;
mFittingModes = other.mFittingModes;
mOrientations = other.mOrientations;
final int defaultCount = other.mDefaults.length;
for (int i = 0; i < defaultCount; i++) {
mDefaults[i] = other.mDefaults[i];
}
mDefaultMargins = other.mDefaultMargins;
}
/**
@@ -159,7 +77,16 @@ public final class PrinterInfo implements Parcelable {
}
/**
* Gets the status of the printer.
* Get the printer name.
*
* @return The printer name.
*/
public String getName() {
return mName;
}
/**
* Gets the printer status.
*
* @return The status.
*/
@@ -168,184 +95,29 @@ public final class PrinterInfo implements Parcelable {
}
/**
* Gets the supported media sizes.
* Gets the printer description.
*
* @return The supported media sizes.
* @return The description.
*/
public List<MediaSize> getMediaSizes() {
return mMediaSizes;
public String getDescription() {
return mDescription;
}
/**
* Gets the supported resolutions.
* Gets the printer capabilities.
*
* @return The supported resolutions.
* @return The capabilities.
*/
public List<Resolution> getResolutions() {
return mResolutions;
}
/**
* Gets the minimal supported margins.
*
* @return The minimal margins.
*/
public Margins getMinMargins() {
return mMinMargins;
}
/**
* Gets the available input trays.
*
* @return The input trays.
*/
public List<Tray> getInputTrays() {
return mInputTrays;
}
/**
* Gets the available output trays.
*
* @return The output trays.
*/
public List<Tray> getOutputTrays() {
return mOutputTrays;
}
/**
* Gets the supported duplex modes.
*
* @return The duplex modes.
*
* @see PrintAttributes#DUPLEX_MODE_NONE
* @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
* @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
*/
public int getDuplexModes() {
return mDuplexModes;
}
/**
* Gets the supported color modes.
*
* @return The color modes.
*
* @see PrintAttributes#COLOR_MODE_COLOR
* @see PrintAttributes#COLOR_MODE_MONOCHROME
*/
public int getColorModes() {
return mColorModes;
}
/**
* Gets the supported fitting modes.
*
* @return The fitting modes.
*
* @see PrintAttributes#FITTING_MODE_NONE
* @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
*/
public int getFittingModes() {
return mFittingModes;
}
/**
* Gets the supported orientations.
*
* @return The orientations.
*
* @see PrintAttributes#ORIENTATION_PORTRAIT
* @see PrintAttributes#ORIENTATION_LANDSCAPE
*/
public int getOrientations() {
return mOrientations;
}
/**
* Gets the default print attributes.
*
* @param outAttributes The attributes to populated.
*/
public void getDefaults(PrintAttributes outAttributes) {
outAttributes.clear();
outAttributes.setMargins(mDefaultMargins);
final int mediaSizeIndex = mDefaults[PROPERTY_MEDIA_SIZE];
if (mediaSizeIndex >= 0) {
outAttributes.setMediaSize(mMediaSizes.get(mediaSizeIndex));
}
final int resolutionIndex = mDefaults[PROPERTY_RESOLUTION];
if (resolutionIndex >= 0) {
outAttributes.setResolution(mResolutions.get(resolutionIndex));
}
final int inputTrayIndex = mDefaults[PROPERTY_INPUT_TRAY];
if (inputTrayIndex >= 0) {
outAttributes.setInputTray(mInputTrays.get(inputTrayIndex));
}
final int outputTrayIndex = mDefaults[PROPERTY_OUTPUT_TRAY];
if (outputTrayIndex >= 0) {
outAttributes.setOutputTray(mOutputTrays.get(outputTrayIndex));
}
final int duplexMode = mDefaults[PROPERTY_DUPLEX_MODE];
if (duplexMode > 0) {
outAttributes.setDuplexMode(duplexMode);
}
final int colorMode = mDefaults[PROPERTY_COLOR_MODE];
if (colorMode > 0) {
outAttributes.setColorMode(mColorModes & colorMode);
}
final int fittingMode = mDefaults[PROPERTY_FITTING_MODE];
if (fittingMode > 0) {
outAttributes.setFittingMode(fittingMode);
}
final int orientation = mDefaults[PROPERTY_ORIENTATION];
if (orientation > 0) {
outAttributes.setOrientation(orientation);
}
}
/**
* Gets whether this printer info is fully-populated, i.e. whether
* all required attributes are specified. See the {@link Builder}
* documentation for which attributes are required.
*
* @return Whether this info has all required attributes.
*/
public boolean hasAllRequiredAttributes() {
return (mMediaSizes != null && !mMediaSizes.isEmpty()
&& mResolutions != null && !mResolutions.isEmpty()
&& mColorModes != 0 || mOrientations != 0
&& mDefaults[PROPERTY_MEDIA_SIZE] != DEFAULT_UNDEFINED
&& mDefaults[PROPERTY_RESOLUTION] != DEFAULT_UNDEFINED
&& mDefaults[PROPERTY_COLOR_MODE] != DEFAULT_UNDEFINED
&& mDefaults[PROPERTY_ORIENTATION] != DEFAULT_UNDEFINED);
public PrinterCapabilitiesInfo getCapabilities() {
return mCapabilities;
}
private PrinterInfo(Parcel parcel) {
mId = parcel.readParcelable(null);
mName = parcel.readString();
mStatus = parcel.readInt();
mMinMargins = readMargins(parcel);
readMediaSizes(parcel);
readResolutions(parcel);
mInputTrays = readInputTrays(parcel);
mOutputTrays = readOutputTrays(parcel);
mColorModes = parcel.readInt();
mDuplexModes = parcel.readInt();
mFittingModes = parcel.readInt();
mOrientations = parcel.readInt();
readDefaults(parcel);
mDefaultMargins = readMargins(parcel);
mDescription = parcel.readString();
mCapabilities = parcel.readParcelable(null);
}
@Override
@@ -356,40 +128,21 @@ public final class PrinterInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mId, flags);
parcel.writeString(mName);
parcel.writeInt(mStatus);
writeMargins(mMinMargins, parcel);
writeMediaSizes(parcel);
writeResolutions(parcel);
writeInputTrays(parcel);
writeOutputTrays(parcel);
parcel.writeInt(mColorModes);
parcel.writeInt(mDuplexModes);
parcel.writeInt(mFittingModes);
parcel.writeInt(mOrientations);
writeDefaults(parcel);
writeMargins(mDefaultMargins, parcel);
parcel.writeString(mDescription);
parcel.writeParcelable(mCapabilities, flags);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mId == null) ? 0 : mId.hashCode());
result = prime * result + ((mId != null) ? mId.hashCode() : 0);
result = prime * result + ((mName != null) ? mName.hashCode() : 0);
result = prime * result + mStatus;
result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode());
result = prime * result + ((mResolutions == null) ? 0 : mResolutions.hashCode());
result = prime * result + ((mInputTrays == null) ? 0 : mInputTrays.hashCode());
result = prime * result + ((mOutputTrays == null) ? 0 : mOutputTrays.hashCode());
result = prime * result + mDuplexModes;
result = prime * result + mColorModes;
result = prime * result + mFittingModes;
result = prime * result + mOrientations;
result = prime * result + Arrays.hashCode(mDefaults);
result = prime * result + ((mDefaultMargins == null) ? 0 : mDefaultMargins.hashCode());
result = prime * result + ((mDescription != null) ? mDescription.hashCode() : 0);
result = prime * result + ((mCapabilities != null) ? mCapabilities.hashCode() : 0);
return result;
}
@@ -412,64 +165,20 @@ public final class PrinterInfo implements Parcelable {
} else if (!mId.equals(other.mId)) {
return false;
}
if (!TextUtils.equals(mName, other.mName)) {
return false;
}
if (mStatus != other.mStatus) {
return false;
}
if (mMinMargins == null) {
if (other.mMinMargins != null) {
if (!TextUtils.equals(mDescription, other.mDescription)) {
return false;
}
if (mCapabilities == null) {
if (other.mCapabilities != null) {
return false;
}
} else if (!mMinMargins.equals(other.mMinMargins)) {
return false;
}
if (mMediaSizes == null) {
if (other.mMediaSizes != null) {
return false;
}
} else if (!mMediaSizes.equals(other.mMediaSizes)) {
return false;
}
if (mResolutions == null) {
if (other.mResolutions != null) {
return false;
}
} else if (!mResolutions.equals(other.mResolutions)) {
return false;
}
if (mInputTrays == null) {
if (other.mInputTrays != null) {
return false;
}
} else if (!mInputTrays.equals(other.mInputTrays)) {
return false;
}
if (mOutputTrays == null) {
if (other.mOutputTrays != null) {
return false;
}
} else if (!mOutputTrays.equals(other.mOutputTrays)) {
return false;
}
if (mDuplexModes != other.mDuplexModes) {
return false;
}
if (mColorModes != other.mColorModes) {
return false;
}
if (mFittingModes != other.mFittingModes) {
return false;
}
if (mOrientations != other.mOrientations) {
return false;
}
if (!Arrays.equals(mDefaults, other.mDefaults)) {
return false;
}
if (mDefaultMargins == null) {
if (other.mDefaultMargins != null) {
return false;
}
} else if (!mDefaultMargins.equals(other.mDefaultMargins)) {
} else if (!mCapabilities.equals(other.mCapabilities)) {
return false;
}
return true;
@@ -479,434 +188,83 @@ public final class PrinterInfo implements Parcelable {
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PrinterInfo{");
builder.append(mId).append(", \"");
builder.append("id=").append(mId);
builder.append(", name=").append(mName);
builder.append(", status=").append(mStatus);
builder.append(", description=").append(mDescription);
builder.append(", capabilities=").append(mCapabilities);
builder.append("\"}");
return builder.toString();
}
private void writeMediaSizes(Parcel parcel) {
if (mMediaSizes == null) {
parcel.writeInt(0);
return;
}
final int mediaSizeCount = mMediaSizes.size();
parcel.writeInt(mediaSizeCount);
for (int i = 0; i < mediaSizeCount; i++) {
mMediaSizes.get(i).writeToParcel(parcel);
}
}
private void readMediaSizes(Parcel parcel) {
final int mediaSizeCount = parcel.readInt();
if (mediaSizeCount > 0 && mMediaSizes == null) {
mMediaSizes = new ArrayList<MediaSize>();
}
for (int i = 0; i < mediaSizeCount; i++) {
mMediaSizes.add(MediaSize.createFromParcel(parcel));
}
}
private void writeResolutions(Parcel parcel) {
if (mResolutions == null) {
parcel.writeInt(0);
return;
}
final int resolutionCount = mResolutions.size();
parcel.writeInt(resolutionCount);
for (int i = 0; i < resolutionCount; i++) {
mResolutions.get(i).writeToParcel(parcel);
}
}
private void readResolutions(Parcel parcel) {
final int resolutionCount = parcel.readInt();
if (resolutionCount > 0 && mResolutions == null) {
mResolutions = new ArrayList<Resolution>();
}
for (int i = 0; i < resolutionCount; i++) {
mResolutions.add(Resolution.createFromParcel(parcel));
}
}
private void writeMargins(Margins margins, Parcel parcel) {
if (margins == null) {
parcel.writeInt(0);
} else {
parcel.writeInt(1);
margins.writeToParcel(parcel);
}
}
private Margins readMargins(Parcel parcel) {
return (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
}
private void writeInputTrays(Parcel parcel) {
if (mInputTrays == null) {
parcel.writeInt(0);
return;
}
final int inputTrayCount = mInputTrays.size();
parcel.writeInt(inputTrayCount);
for (int i = 0; i < inputTrayCount; i++) {
mInputTrays.get(i).writeToParcel(parcel);
}
}
private List<Tray> readInputTrays(Parcel parcel) {
final int inputTrayCount = parcel.readInt();
if (inputTrayCount <= 0) {
return null;
}
List<Tray> inputTrays = new ArrayList<Tray>(inputTrayCount);
for (int i = 0; i < inputTrayCount; i++) {
inputTrays.add(Tray.createFromParcel(parcel));
}
return inputTrays;
}
private void writeOutputTrays(Parcel parcel) {
if (mOutputTrays == null) {
parcel.writeInt(0);
return;
}
final int outputTrayCount = mOutputTrays.size();
parcel.writeInt(outputTrayCount);
for (int i = 0; i < outputTrayCount; i++) {
mOutputTrays.get(i).writeToParcel(parcel);
}
}
private List<Tray> readOutputTrays(Parcel parcel) {
final int outputTrayCount = parcel.readInt();
if (outputTrayCount <= 0) {
return null;
}
List<Tray> outputTrays = new ArrayList<Tray>(outputTrayCount);
for (int i = 0; i < outputTrayCount; i++) {
outputTrays.add(Tray.createFromParcel(parcel));
}
return outputTrays;
}
private void readDefaults(Parcel parcel) {
final int defaultCount = parcel.readInt();
for (int i = 0; i < defaultCount; i++) {
mDefaults[i] = parcel.readInt();
}
}
private void writeDefaults(Parcel parcel) {
final int defaultCount = mDefaults.length;
parcel.writeInt(defaultCount);
for (int i = 0; i < defaultCount; i++) {
parcel.writeInt(mDefaults[i]);
}
}
/**
* Builder for creating of a {@link PrinterInfo}. This class is responsible
* to enforce that all required attributes have at least one default value.
* In other words, this class creates only well-formed {@link PrinterInfo}s.
* <p>
* Look at the individual methods for a reference whether a property is
* required or if it is optional.
* </p>
* Builder for creating of a {@link PrinterInfo}.
*/
public static final class Builder {
private final PrinterInfo mPrototype;
/**
* Creates a new instance.
* Constructor.
*
* @param printerId The printer id. Cannot be null.
*
* @throws IllegalArgumentException If the printer id is null.
* @param name The printer name. Cannot be empty.
* @param status The printer status. Must be a valid status.
*/
public Builder(PrinterId printerId) {
public Builder(PrinterId printerId, String name, int status) {
if (printerId == null) {
throw new IllegalArgumentException("printerId cannot be null.");
}
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("name cannot be empty.");
}
if (!isValidStatus(status)) {
throw new IllegalArgumentException("status is invalid.");
}
mPrototype = new PrinterInfo();
mPrototype.mId = printerId;
}
/**
* Sets the printer status.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param status The status.
* @return This builder.
*/
public Builder setStatus(int status) {
mPrototype.mName = name;
mPrototype.mStatus = status;
}
/**
* Constructor.
*
* @param prototype Prototype from which to start building.
*/
public Builder(PrinterInfo prototype) {
mPrototype = prototype;
}
/**
* Sets the printer name.
*
* @param name The name.
* @return This builder.
*/
public Builder setName(String name) {
mPrototype.mName = name;
return this;
}
/**
* Adds a supported media size.
* <p>
* <strong>Required:</strong> Yes
* </p>
* Sets the printer description.
*
* @param mediaSize A media size.
* @param isDefault Whether this is the default.
* @param description The description.
* @return This builder.
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.MediaSize
*/
public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
if (mPrototype.mMediaSizes == null) {
mPrototype.mMediaSizes = new ArrayList<MediaSize>();
}
final int insertionIndex = mPrototype.mMediaSizes.size();
mPrototype.mMediaSizes.add(mediaSize);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
mPrototype.mDefaults[PROPERTY_MEDIA_SIZE] = insertionIndex;
}
public Builder setDescription(String description) {
mPrototype.mDescription = description;
return this;
}
/**
* Adds a supported resolution.
* <p>
* <strong>Required:</strong> Yes
* </p>
* Sets the printer capabilities.
*
* @param resolution A resolution.
* @param isDefault Whether this is the default.
* @param capabilities The capabilities.
* @return This builder.
*
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.Resolution
*/
public Builder addResolution(Resolution resolution, boolean isDefault) {
if (mPrototype.mResolutions == null) {
mPrototype.mResolutions = new ArrayList<Resolution>();
}
final int insertionIndex = mPrototype.mResolutions.size();
mPrototype.mResolutions.add(resolution);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
mPrototype.mDefaults[PROPERTY_RESOLUTION] = insertionIndex;
}
return this;
}
/**
* Sets the minimal margins.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param margins The margins.
* @param defaultMargins The default margins.
* @return This builder.
*
* @see PrintAttributes.Margins
*/
public Builder setMinMargins(Margins margins, Margins defaultMargins) {
if (margins.getLeftMils() > defaultMargins.getLeftMils()
|| margins.getTopMils() > defaultMargins.getTopMils()
|| margins.getRightMils() < defaultMargins.getRightMils()
|| margins.getBottomMils() < defaultMargins.getBottomMils()) {
throw new IllegalArgumentException("Default margins"
+ " cannot be outside of the min margins.");
}
mPrototype.mMinMargins = margins;
mPrototype.mDefaultMargins = defaultMargins;
return this;
}
/**
* Adds an input tray.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param inputTray A tray.
* @param isDefault Whether this is the default.
* @return This builder.
*
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.Tray
*/
public Builder addInputTray(Tray inputTray, boolean isDefault) {
if (mPrototype.mInputTrays == null) {
mPrototype.mInputTrays = new ArrayList<Tray>();
}
final int insertionIndex = mPrototype.mInputTrays.size();
mPrototype.mInputTrays.add(inputTray);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_INPUT_TRAY);
mPrototype.mDefaults[PROPERTY_INPUT_TRAY] = insertionIndex;
}
return this;
}
/**
* Adds an output tray.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param outputTray A tray.
* @param isDefault Whether this is the default.
* @return This builder.
*
* @throws IllegalArgumentException If set as default and there
* is already a default.
*
* @see PrintAttributes.Tray
*/
public Builder addOutputTray(Tray outputTray, boolean isDefault) {
if (mPrototype.mOutputTrays == null) {
mPrototype.mOutputTrays = new ArrayList<Tray>();
}
final int insertionIndex = mPrototype.mOutputTrays.size();
mPrototype.mOutputTrays.add(outputTray);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_OUTPUT_TRAY);
mPrototype.mDefaults[PROPERTY_OUTPUT_TRAY] = insertionIndex;
}
return this;
}
/**
* Sets the color modes.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param colorModes The color mode bit mask.
* @param defaultColorMode The default color mode.
* @return This builder.
*
* @throws IllegalArgumentException If color modes contains an invalid
* mode bit or if the default color mode is invalid.
*
* @see PrintAttributes#COLOR_MODE_COLOR
* @see PrintAttributes#COLOR_MODE_MONOCHROME
*/
public Builder setColorModes(int colorModes, int defaultColorMode) {
int currentModes = colorModes;
while (currentModes > 0) {
final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
currentModes &= ~currentMode;
PrintAttributes.enforceValidColorMode(currentMode);
}
if ((colorModes & defaultColorMode) == 0) {
throw new IllegalArgumentException("Default color mode not in color modes.");
}
PrintAttributes.enforceValidColorMode(colorModes);
mPrototype.mColorModes = colorModes;
mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
return this;
}
/**
* Set the duplex modes.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param duplexModes The duplex mode bit mask.
* @param defaultDuplexMode The default duplex mode.
* @return This builder.
*
* @throws IllegalArgumentException If duplex modes contains an invalid
* mode bit or if the default duplex mode is invalid.
*
* @see PrintAttributes#DUPLEX_MODE_NONE
* @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
* @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
*/
public Builder setDuplexModes(int duplexModes, int defaultDuplexMode) {
int currentModes = duplexModes;
while (currentModes > 0) {
final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
currentModes &= ~currentMode;
PrintAttributes.enforceValidDuplexMode(currentMode);
}
if ((duplexModes & defaultDuplexMode) == 0) {
throw new IllegalArgumentException("Default duplex mode not in duplex modes.");
}
PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
mPrototype.mDuplexModes = duplexModes;
mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode;
return this;
}
/**
* Sets the fitting modes.
* <p>
* <strong>Required:</strong> No
* </p>
*
* @param fittingModes The fitting mode bit mask.
* @param defaultFittingMode The default fitting mode.
* @return This builder.
*
* @throws IllegalArgumentException If fitting modes contains an invalid
* mode bit or if the default fitting mode is invalid.
*
* @see PrintAttributes#FITTING_MODE_NONE
* @see PrintAttributes#FITTING_MODE_FIT_TO_PAGE
*/
public Builder setFittingModes(int fittingModes, int defaultFittingMode) {
int currentModes = fittingModes;
while (currentModes > 0) {
final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
currentModes &= ~currentMode;
PrintAttributes.enfoceValidFittingMode(currentMode);
}
if ((fittingModes & defaultFittingMode) == 0) {
throw new IllegalArgumentException("Default fitting mode not in fiting modes.");
}
PrintAttributes.enfoceValidFittingMode(defaultFittingMode);
mPrototype.mFittingModes = fittingModes;
mPrototype.mDefaults[PROPERTY_FITTING_MODE] = defaultFittingMode;
return this;
}
/**
* Sets the orientations.
* <p>
* <strong>Required:</strong> Yes
* </p>
*
* @param orientations The orientation bit mask.
* @param defaultOrientation The default orientation.
* @return This builder.
*
* @throws IllegalArgumentException If orientations contains an invalid
* mode bit or if the default orientation is invalid.
*
* @see PrintAttributes#ORIENTATION_PORTRAIT
* @see PrintAttributes#ORIENTATION_LANDSCAPE
*/
public Builder setOrientations(int orientations, int defaultOrientation) {
int currentOrientaions = orientations;
while (currentOrientaions > 0) {
final int currentOrnt = (1 << Integer.numberOfTrailingZeros(currentOrientaions));
currentOrientaions &= ~currentOrnt;
PrintAttributes.enforceValidOrientation(currentOrnt);
}
if ((orientations & defaultOrientation) == 0) {
throw new IllegalArgumentException("Default orientation not in orientations.");
}
PrintAttributes.enforceValidOrientation(defaultOrientation);
mPrototype.mOrientations = orientations;
mPrototype.mDefaults[PROPERTY_ORIENTATION] = defaultOrientation;
public Builder setCapabilities(PrinterCapabilitiesInfo capabilities) {
mPrototype.mCapabilities = capabilities;
return this;
}
@@ -919,10 +277,8 @@ public final class PrinterInfo implements Parcelable {
return new PrinterInfo(mPrototype);
}
private void throwIfDefaultAlreadySpecified(int propertyIndex) {
if (mPrototype.mDefaults[propertyIndex] != DEFAULT_UNDEFINED) {
throw new IllegalArgumentException("Default already specified.");
}
private boolean isValidStatus(int status) {
return (status == PrinterInfo.STATUS_READY);
}
}

View File

@@ -10,16 +10,14 @@
* 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
* See the License for the specific languag<EFBFBD>e governing permissions and
* limitations under the License.
*/
package android.printservice;
import android.os.ICancellationSignal;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.printservice.IPrintServiceClient;
/**
@@ -29,9 +27,7 @@ import android.printservice.IPrintServiceClient;
*/
oneway interface IPrintService {
void setClient(IPrintServiceClient client);
void onRequestUpdatePrinters(in List<PrinterId> printerIds);
void onRequestCancelPrintJob(in PrintJobInfo printJobInfo);
void requestCancelPrintJob(in PrintJobInfo printJobInfo);
void onPrintJobQueued(in PrintJobInfo printJobInfo);
void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
void onStopPrinterDiscovery();
void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
}

View File

@@ -55,14 +55,14 @@ public final class PrintDocument {
}
/**
* Gets the data associated with this document. It is a responsibility of the
* client to open a stream to the returned file descriptor and fully read the
* data.
* Gets the data associated with this document.
* <p>
* <strong>Note:</strong> It is your responsibility to close the file descriptor.
* <strong>Note: </strong> It is a responsibility of the client to open a
* stream to the returned file descriptor, fully read the data, and close
* the file descriptor.
* </p>
*
* @return A file descriptor for reading the data or <code>null</code>.
* @return A file descriptor for reading the data.
*/
public FileDescriptor getData() {
ParcelFileDescriptor source = null;

View File

@@ -21,9 +21,9 @@ import android.print.PrintJobInfo;
import android.util.Log;
/**
* This class represents a print job from the perspective of a
* print service. It provides APIs for observing the print job
* state and performing operations on the print job.
* This class represents a print job from the perspective of a print
* service. It provides APIs for observing the print job state and
* performing operations on the print job.
*/
public final class PrintJob {
@@ -38,7 +38,8 @@ public final class PrintJob {
PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
mCachedInfo = jobInfo;
mPrintServiceClient = client;
mDocument = new PrintDocument(mCachedInfo.getId(), client, jobInfo.getDocumentInfo());
mDocument = new PrintDocument(mCachedInfo.getId(), client,
jobInfo.getDocumentInfo());
}
/**
@@ -77,7 +78,7 @@ public final class PrintJob {
}
/**
* Gets the document of this print job.
* Gets the printed document.
*
* @return The document.
*/
@@ -87,11 +88,12 @@ public final class PrintJob {
/**
* Gets whether this print job is queued. Such a print job is
* ready to be printed and can be started.
* ready to be printed and can be started or cancelled.
*
* @return Whether the print job is queued.
*
* @see #start()
* @see #cancel()
*/
public boolean isQueued() {
return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
@@ -111,6 +113,42 @@ public final class PrintJob {
return getInfo().getState() == PrintJobInfo.STATE_STARTED;
}
/**
* Gets whether this print job is completed. Such a print job
* is successfully printed. This is a final state.
*
* @return Whether the print job is completed.
*
* @see #complete()
*/
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. This is a final state.
*
* @return Whether the print job is failed.
*
* @see #fail(CharSequence)
*/
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.
*
* @return Whether the print job is cancelled.
*
* @see #cancel()
*/
public boolean isCancelled() {
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
}
/**
* Starts the print job. You should call this method if {@link
* #isQueued()} returns true and you started printing.
@@ -163,12 +201,13 @@ public final class PrintJob {
/**
* Cancels the print job. You should call this method if {@link
* #isQueued()} or {@link #isStarted()} returns true and you canceled
* the print job as a response to a call to {@link PrintService
* #onRequestCancelPrintJob(PrintJob)}.
* the print job as a response to a call to {@link
* PrintService#onRequestCancelPrintJob(PrintJob)}.
*
* @return Whether the job as canceled.
* @return Whether the job is canceled.
*
* @see #isStarted()
* @see #isQueued()
*/
public boolean cancel() {
if (isQueued() || isStarted()) {

View File

@@ -25,10 +25,9 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.util.Log;
import java.util.ArrayList;
@@ -37,84 +36,82 @@ import java.util.List;
/**
* <p>
* This is the base class for implementing print services. A print service
* knows how to discover and interact one or more printers via one or more
* protocols.
* This is the base class for implementing print services. A print service knows
* how to discover and interact one or more printers via one or more protocols.
* </p>
* <h3>Printer discovery</h3>
* <p>
* A print service is responsible for discovering and reporting printers.
* A printer discovery period starts with a call to
* {@link #onStartPrinterDiscovery()} and ends with a call to
* {@link #onStopPrinterDiscovery()}. During a printer discovery
* period the print service reports newly discovered printers by
* calling {@link #addDiscoveredPrinters(List)} and reports added printers
* that disappeared by calling {@link #removeDiscoveredPrinters(List)}.
* Calls to {@link #addDiscoveredPrinters(List)} and
* {@link #removeDiscoveredPrinters(List)} before a call to
* {@link #onStartPrinterDiscovery()} and after a call to
* {@link #onStopPrinterDiscovery()} are a no-op.
* A print service is responsible for discovering printers, adding discovered printers,
* removing added printers, and updating added printers. When the system is interested
* in printers managed by your service it will call {@link
* #onCreatePrinterDiscoverySession()} from which you must return a new {@link
* PrinterDiscoverySession} instance. The returned session encapsulates the interaction
* between the system and your service during printer discovery. For description of this
* interaction refer to the documentation for {@link PrinterDiscoverySession}.
* </p>
* <p>
* For every printer discovery period all printers have to be added. Each
* printer known to this print service should be added only once during a
* discovery period, unless it was added and then removed before that.
* Only an already added printer can be removed.
* For every printer discovery session all printers have to be added since system does
* not retain printers across sessions. Hence, each printer known to this print service
* should be added only once during a discovery session. Only an already added printer
* can be removed or updated. Removed printers can be added again.
* </p>
* <h3>Print jobs</h3>
* <p>
* When a new print job targeted to the printers managed by this print
* service is queued, i.e. ready for processing by the print service,
* a call to {@link #onPrintJobQueued(PrintJob)} is made and the print
* service may handle it immediately or schedule that for an appropriate
* time in the future. The list of all print jobs for this service
* are be available by calling {@link #getPrintJobs()}.
* When a new print job targeted to a printer managed by this print service is is queued,
* i.e. ready for processing by the print service, you will receive a call to {@link
* #onPrintJobQueued(PrintJob)}. The print service may handle the print job immediately
* or schedule that for an appropriate time in the future. The list of all active print
* jobs for this service is obtained by calling {@link #getActivePrintJobs()}. Active
* print jobs are ones that are queued or started.
* </p>
* <p>
* A print service is responsible for setting the print job state as
* appropriate while processing it. Initially, a print job is in a
* {@link PrintJobInfo#STATE_QUEUED} state which means that the data to
* be printed is spooled by the system and the print service can obtain
* that data by calling {@link PrintJob#getDocument()}. A queued print
* job's {@link PrintJob#isQueued()} method returns true.
* A print service is responsible for setting a print job's state as appropriate
* while processing it. Initially, a print job is queued, i.e. {@link PrintJob#isQueued()
* PrintJob.isQueued()} returns true, which means that the document to be printed is
* spooled by the system and the print service can begin processing it. You can obtain
* the printed document by calling {@link PrintJob#getDocument() PrintJob.getDocument()}
* whose data is accessed via {@link PrintDocument#getData() PrintDocument.getData()}.
* After the print service starts printing the data it should set the print job's
* state to started by calling {@link PrintJob#start()} after which
* {@link PrintJob#isStarted() PrintJob.isStarted()} would return true. Upon successful
* completion, the print job should be marked as completed by calling {@link
* PrintJob#complete() PrintJob.complete()} after which {@link PrintJob#isCompleted()
* PrintJob.isCompleted()} would return true. In case of a failure, the print job should
* be marked as failed by calling {@link PrintJob#fail(CharSequence) PrintJob.fail(
* CharSequence)} after which {@link PrintJob#isFailed() PrintJob.isFailed()} would
* return true.
* </p>
* <p>
* After the print service starts printing the data it should set the
* print job state to {@link PrintJobInfo#STATE_STARTED} by calling
* {@link PrintJob#start()}. Upon successful completion, the print job
* state has to be set to {@link PrintJobInfo#STATE_COMPLETED} by calling
* {@link PrintJob#complete()}. In case of a failure, the print job
* state should be set to {@link PrintJobInfo#STATE_FAILED} by calling
* {@link PrintJob#fail(CharSequence)}. If a print job is in a
* {@link PrintJobInfo#STATE_STARTED} state, i.e. {@link PrintJob#isStarted()}
* return true, and the user requests to cancel it, the print service will
* receive a call to {@link #onRequestCancelPrintJob(PrintJob)} which
* requests from the service to do a best effort in canceling the job. In
* case the job is successfully canceled, its state has to be set to
* {@link PrintJobInfo#STATE_CANCELED}. by calling {@link PrintJob#cancel()}.
* If a print job is queued or started and the user requests to cancel it, the print
* service will receive a call to {@link #onRequestCancelPrintJob(PrintJob)} which
* requests from the service to do best effort in canceling the job. In case the job
* is successfully canceled, its state has to be marked as cancelled by calling {@link
* PrintJob#cancel() PrintJob.cancel()} after which {@link PrintJob#isCancelled()
* PrintJob.isCacnelled()} would return true.
* </p>
* <h3>Lifecycle</h3>
* <p>
* The lifecycle of a print service is managed exclusively by the system
* and follows the established service lifecycle. Additionally, starting
* or stopping a print service is triggered exclusively by an explicit
* user action through enabling or disabling it in the device settings.
* After the system binds to a print service, it calls {@link #onConnected()}.
* This method can be overriden by clients to perform post binding setup.
* Also after the system unbinds from a print service, it calls
* {@link #onDisconnected()}. This method can be overriden by clients to
* perform post unbinding cleanup.
* The lifecycle of a print service is managed exclusively by the system and follows
* the established service lifecycle. Additionally, starting or stopping a print service
* is triggered exclusively by an explicit user action through enabling or disabling it
* in the device settings. After the system binds to a print service, it calls {@link
* #onConnected()}. This method can be overriden by clients to perform post binding setup.
* Also after the system unbinds from a print service, it calls {@link #onDisconnected()}.
* This method can be overriden by clients to perform post unbinding cleanup. Your should
* not do any work after the system disconnected from your print service since the
* service can be killed at any time to reclaim memory. The system will not disconnect
* from a print service if there are active print jobs for the printers managed by it.
* </p>
* <h3>Declaration</h3>
* <p>
* A print service is declared as any other service in an AndroidManifest.xml
* but it must also specify that it handles the {@link android.content.Intent}
* with action {@link #SERVICE_INTERFACE}. Failure to declare this intent
* will cause the system to ignore the print service. Additionally, a print
* service must request the {@link android.Manifest.permission#BIND_PRINT_SERVICE}
* permission to ensure that only the system can bind to it. Failure to
* declare this intent will cause the system to ignore the print service.
* Following is an example declaration:
* A print service is declared as any other service in an AndroidManifest.xml but it must
* also specify that it handles the {@link android.content.Intent} with action {@link
* #SERVICE_INTERFACE android.printservice.PrintService}. Failure to declare this intent
* will cause the system to ignore the print service. Additionally, a print service must
* request the {@link android.Manifest.permission#BIND_PRINT_SERVICE
* android.permission.BIND_PRINT_SERVICE} permission to ensure that only the system can
* bind to it. Failure to declare this intent will cause the system to ignore the print
* service. Following is an example declaration:
* </p>
* <pre>
* &lt;service android:name=".MyPrintService"
@@ -127,17 +124,15 @@ import java.util.List;
* </pre>
* <h3>Configuration</h3>
* <p>
* A print service can be configured by specifying an optional settings
* activity which exposes service specific options, an optional add
* prints activity which is used for manual addition of printers, vendor
* name ,etc. It is a responsibility of the system to launch the settings
* and add printers activities when appropriate.
* A print service can be configured by specifying an optional settings activity which
* exposes service specific settings, an optional add printers activity which is used for
* manual addition of printers, vendor name ,etc. It is a responsibility of the system
* to launch the settings and add printers activities when appropriate.
* </p>
* <p>
* A print service is configured by providing a
* {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
* the service. A service declaration with a meta-data tag is presented
* below:
* A print service is configured by providing a {@link #SERVICE_META_DATA meta-data}
* entry in the manifest when declaring the service. A service declaration with a meta-data
* tag is presented below:
* <pre> &lt;service android:name=".MyPrintService"
* android:permission="android.permission.BIND_PRINT_SERVICE"&gt;
* &lt;intent-filter&gt;
@@ -147,8 +142,9 @@ import java.util.List;
* &lt;/service&gt;</pre>
* </p>
* <p>
* For more details refer to {@link #SERVICE_META_DATA} and
* <code>&lt;{@link android.R.styleable#PrintService print-service}&gt;</code>.
* For more details for how to configure your print service via the meta-data refer to
* {@link #SERVICE_META_DATA} and <code>&lt;{@link android.R.styleable#PrintService
* print-service}&gt;</code>.
* </p>
*/
public abstract class PrintService extends Service {
@@ -157,21 +153,25 @@ public abstract class PrintService extends Service {
/**
* The {@link Intent} action that must be declared as handled by a service
* in its manifest to allow the system to recognize it as a print service.
* in its manifest for the system to recognize it as a print service.
*/
public static final String SERVICE_INTERFACE = "android.printservice.PrintService";
/**
* Name under which a PrintService component publishes additional information
* about itself. This meta-data must reference an XML resource containing a
* <code>&lt;{@link android.R.styleable#PrintService print-service}&gt;</code>
* tag. This is a a sample XML file configuring a print service:
* Name under which a {@link PrintService} component publishes additional information
* about itself. This meta-data must reference a XML resource containing a <code>
* &lt;{@link android.R.styleable#PrintService print-service}&gt;</code> tag. This is
* a sample XML file configuring a print service:
* <pre> &lt;print-service
* android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
* andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
* /&gt;</pre>
* <p>
* For detailed configuration options that can be specified via the meta-data
* refer to {@link android.R.styleable#PrintService android.R.styleable.PrintService}.
* </p>
*/
public static final String SERVICE_META_DATA = "android.printservice";
@@ -181,12 +181,12 @@ public abstract class PrintService extends Service {
private IPrintServiceClient mClient;
private IPrinterDiscoveryObserver mDiscoveryObserver;
private int mLastSessionId = -1;
@Override
protected void attachBaseContext(Context base) {
protected final void attachBaseContext(Context base) {
super.attachBaseContext(base);
mHandler = new MyHandler(base.getMainLooper());
mHandler = new ServiceHandler(base.getMainLooper());
}
/**
@@ -204,203 +204,47 @@ public abstract class PrintService extends Service {
}
/**
* Callback requesting from this service to start printer discovery.
* At the end of the printer discovery period the system will call
* {@link #onStopPrinterDiscovery()}. Discovered printers should be
* reported by calling {@link #addDiscoveredPrinters(List)} and reported
* ones that disappear should be reported by calling
* {@link #removeDiscoveredPrinters(List)}.
* Callback asking you to create a new {@link PrinterDiscoverySession}.
*
* @see #onStopPrinterDiscovery()
* @see #addDiscoveredPrinters(List)
* @see #removeDiscoveredPrinters(List)
* @see #updateDiscoveredPrinters(List)
* @see PrinterDiscoverySession
*/
protected abstract void onStartPrinterDiscovery();
protected abstract PrinterDiscoverySession onCreatePrinterDiscoverySession();
/**
* Callback requesting from this service to stop printer discovery.
*
* @see #onStartPrinterDiscovery()
* @see #addDiscoveredPrinters(List)
* @see #removeDiscoveredPrinters(List)
* @see #updateDiscoveredPrinters(List)
*/
protected abstract void onStopPrinterDiscovery();
/**
* Adds discovered printers. This method should be called during a
* printer discovery period, i.e. after a call to
* {@link #onStartPrinterDiscovery()} and before the corresponding
* call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
* <p>
* <strong>Note:</strong> For every printer discovery period all
* printers have to be added. You can call this method as many times as
* necessary during the discovery period but should not pass in already
* added printers. If a printer is already added in the same printer
* discovery period, it will be ignored.
* </p>
* <p>
* A {@link PrinterInfo} can have all of its required attributes specified,
* or not. Whether all attributes are specified can be verified by calling
* {@link PrinterInfo#hasAllRequiredAttributes()}. You can add printers
* regardless if all required attributes are specified. When the system
* (and the user) needs to interact with a printer, you will receive a
* call to {@link #onRequestUpdatePrinters(List)}. If you fail to update
* a printer that was added without all required attributes via calling
* {@link #updateDiscoveredPrinters(List)}, then this printer will be
* ignored, i.e. considered unavailable.
* <p>
*
* @param printers A list with discovered printers.
*
* @see #updateDiscoveredPrinters(List)
* @see #removeDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
*/
public final void addDiscoveredPrinters(List<PrinterInfo> printers) {
final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
observer = mDiscoveryObserver;
}
if (observer != null) {
try {
observer.onPrintersAdded(printers);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error adding discovered printers", re);
}
}
}
/**
* Removes discovered printers given their ids. This method should be called
* during a printer discovery period, i.e. after a call to
* {@link #onStartPrinterDiscovery()} and before the corresponding
* call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
* <p>
* For every printer discovery period all printers have to be added. You
* should remove only printers that were added in this printer discovery
* period by a call to {@link #addDiscoveredPrinters(List)}. You can call
* this method as many times as necessary during the discovery period
* but should not pass in already removed printer ids. If a printer with
* a given id is already removed, it will be ignored.
* </p>
*
* @param printerIds A list with disappeared printer ids.
*
* @see #addDiscoveredPrinters(List)
* @see #updateDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
*/
public final void removeDiscoveredPrinters(List<PrinterId> printerIds) {
final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
observer = mDiscoveryObserver;
}
if (observer != null) {
try {
observer.onPrintersRemoved(printerIds);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error removing discovered printers", re);
}
}
}
/**
* Updates discovered printers that are already added. This method should
* be called during a printer discovery period, i.e. after a call to
* {@link #onStartPrinterDiscovery()} and before the corresponding
* call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
* <p>
* For every printer discovery period all printers have to be added. You
* should update only printers that were added in this printer discovery
* period by a call to {@link #addDiscoveredPrinters(List)}. You can call
* this method as many times as necessary during the discovery period
* but should not try to update already removed or never added printers.
* If a printer is already removed or never added, it will be ignored.
* </p>
*
* @param printers A list with updated printers.
*
* @see #addDiscoveredPrinters(List)
* @see #removeDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
*/
public final void updateDiscoveredPrinters(List<PrinterInfo> printers) {
final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
observer = mDiscoveryObserver;
}
if (observer != null) {
try {
observer.onPrintersUpdated(printers);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error updating discovered printers", re);
}
}
}
/**
* Called when the system will start interacting with a printer
* giving you a change to update it in case some of its capabilities
* have changed. For example, this method will be called when the
* user selects a printer. Hence, it updating this printer should
* be done as quickly as possible in order to achieve maximally
* smooth user experience.
* <p>
* A {@link PrinterInfo} can have all of its required attributes specified,
* or not. Whether all attributes are specified can be verified by calling
* {@link PrinterInfo#hasAllRequiredAttributes()}. You can add printers
* regardless if all required attributes are specified. When the system
* (and the user) needs to interact with a printer, you will receive a
* call to this method. If you fail to update a printer that was added
* without all required attributes via calling
* {@link #updateDiscoveredPrinters(List)}, then this printer will be
* ignored, i.e. considered unavailable.
* </p>
*
* @param printerIds The printers to be updated.
*/
protected void onRequestUpdatePrinters(List<PrinterId> printerIds) {
}
/**
* Called when canceling of a print job is requested. The service
* Called when cancellation of a print job is requested. The service
* should do best effort to fulfill the request. After the cancellation
* is performed, the print job should be set to a cancelled state by
* is performed, the print job should be marked as cancelled state by
* calling {@link PrintJob#cancel()}.
*
* @param printJob The print job to be canceled.
* @param printJob The print job to cancel.
*
* @see PrintJob#cancel() PrintJob.cancel()
* @see PrintJob#isCancelled() PrintJob.isCancelled()
*/
protected void onRequestCancelPrintJob(PrintJob printJob) {
}
protected abstract void onRequestCancelPrintJob(PrintJob printJob);
/**
* Called when there is a queued print job for one of the printers
* managed by this print service. A queued print job is ready for
* processing by a print service which can get the data to be printed
* by calling {@link PrintJob#getDocument()}. This service may start
* processing the passed in print job or schedule handling of queued
* print jobs at a convenient time. The service can get the print
* jobs by a call to {@link #getPrintJobs()} and examine their state
* to find the ones with state {@link PrintJobInfo#STATE_QUEUED} by
* calling {@link PrintJob#isQueued()}.
* managed by this print service.
*
* @param printJob The new queued print job.
*
* @see #getPrintJobs()
* @see PrintJob#isQueued() PrintJob.isQueued()
* @see #getActivePrintJobs()
*/
protected abstract void onPrintJobQueued(PrintJob printJob);
/**
* Gets the print jobs for the printers managed by this service.
* Gets the active print jobs for the printers managed by this service.
* Active print jobs are ones that are not in a final state, i.e. whose
* state is queued or started.
*
* @return The print jobs.
* @return The active print jobs.
*
* @see PrintJob#isQueued() PrintJob.isQueued()
* @see PrintJob#isStarted() PrintJob.isStarted()
*/
public final List<PrintJob> getPrintJobs() {
public final List<PrintJob> getActivePrintJobs() {
final IPrintServiceClient client;
synchronized (mLock) {
client = mClient;
@@ -428,14 +272,14 @@ public abstract class PrintService extends Service {
}
/**
* Generates a global printer id given the printer's locally unique name.
* Generates a global printer id given the printer's locally unique one.
*
* @param printerName The printer name.
* @param localId A locally unique id in the context of your print service.
* @return Global printer id.
*/
public final PrinterId generatePrinterId(String printerName) {
public final PrinterId generatePrinterId(String localId) {
return new PrinterId(new ComponentName(getPackageName(),
getClass().getName()), printerName);
getClass().getName()), localId);
}
@Override
@@ -443,69 +287,58 @@ public abstract class PrintService extends Service {
return new IPrintService.Stub() {
@Override
public void setClient(IPrintServiceClient client) {
mHandler.obtainMessage(MyHandler.MSG_SET_CLEINT, client).sendToTarget();
mHandler.obtainMessage(ServiceHandler.MSG_SET_CLEINT, client)
.sendToTarget();
}
@Override
public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
mHandler.obtainMessage(ServiceHandler.MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION,
observer).sendToTarget();
}
@Override
public void onStopPrinterDiscovery() {
mHandler.sendEmptyMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY);
}
@Override
public void onRequestUpdatePrinters(List<PrinterId> printerIds) {
mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
printerIds).sendToTarget();
}
@Override
public void onRequestCancelPrintJob(PrintJobInfo printJobInfo) {
mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINTJOB,
public void requestCancelPrintJob(PrintJobInfo printJobInfo) {
mHandler.obtainMessage(ServiceHandler.MSG_ON_REQUEST_CANCEL_PRINTJOB,
printJobInfo).sendToTarget();
}
@Override
public void onPrintJobQueued(PrintJobInfo printJobInfo) {
mHandler.obtainMessage(MyHandler.MSG_ON_PRINTJOB_QUEUED,
mHandler.obtainMessage(ServiceHandler.MSG_ON_PRINTJOB_QUEUED,
printJobInfo).sendToTarget();
}
};
}
private final class MyHandler extends Handler {
public static final int MSG_ON_START_PRINTER_DISCOVERY = 1;
public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 2;
private final class ServiceHandler extends Handler {
public static final int MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION = 1;
public static final int MSG_ON_PRINTJOB_QUEUED = 2;
public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 3;
public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 4;
public static final int MSG_ON_PRINTJOB_QUEUED = 5;
public static final int MSG_SET_CLEINT = 6;
public static final int MSG_SET_CLEINT = 4;
public MyHandler(Looper looper) {
public ServiceHandler(Looper looper) {
super(looper, null, true);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
final int action = message.what;
switch (action) {
case MSG_ON_START_PRINTER_DISCOVERY: {
synchronized (mLock) {
mDiscoveryObserver = (IPrinterDiscoveryObserver) message.obj;
case MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION: {
IPrinterDiscoverySessionObserver observer =
(IPrinterDiscoverySessionObserver) message.obj;
PrinterDiscoverySession session = onCreatePrinterDiscoverySession();
if (session == null) {
throw new NullPointerException("session cannot be null");
}
onStartPrinterDiscovery();
} break;
case MSG_ON_STOP_PRINTER_DISCOVERY: {
synchronized (mLock) {
mDiscoveryObserver = null;
if (session.getId() == mLastSessionId) {
throw new IllegalStateException("cannot reuse sessions");
}
mLastSessionId = session.getId();
}
onStopPrinterDiscovery();
session.setObserver(observer);
} break;
case MSG_ON_REQUEST_CANCEL_PRINTJOB: {
@@ -513,11 +346,6 @@ public abstract class PrintService extends Service {
onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
} break;
case MSG_ON_REQUEST_UPDATE_PRINTERS: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
onRequestUpdatePrinters(printerIds);
} break;
case MSG_ON_PRINTJOB_QUEUED: {
PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
onPrintJobQueued(new PrintJob(printJobInfo, mClient));
@@ -527,9 +355,6 @@ public abstract class PrintService extends Service {
IPrintServiceClient client = (IPrintServiceClient) message.obj;
synchronized (mLock) {
mClient = client;
if (client == null) {
mDiscoveryObserver = null;
}
}
if (client != null) {
onConnected();

View File

@@ -0,0 +1,357 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.printservice;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.print.IPrinterDiscoverySessionController;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.List;
/**
* This class encapsulates the interaction between a print service and the
* system during printer discovery. During printer discovery you are responsible
* for adding discovered printers, removing already added printers that
* disappeared, and updating already added printers.
* <p>
* The opening of the session is announced by a call to {@link
* PrinterDiscoverySession#onOpen(List)} at which point you should start printer
* discovery. The closing of the session is announced by a call to {@link
* PrinterDiscoverySession#onClose()} at which point you should stop printer
* discovery. Discovered printers are added by invoking {@link
* PrinterDiscoverySession#addPrinters(List)}. Added printers that disappeared
* are removed by invoking {@link PrinterDiscoverySession#removePrinters(List)}.
* Added printers whose properties or capabilities changed are updated through
* a call to {@link PrinterDiscoverySession#updatePrinters(List)}.
* </p>
* <p>
* The system will make a call to
* {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
* need to update a given printer. It is possible that you add a printer without
* specifying its capabilities. This enables you to avoid querying all
* discovered printers for their capabilities, rather querying the capabilities
* of a printer only if necessary. For example, the system will require that you
* update a printer if it gets selected by the user. If you did not report the
* printer capabilities when adding it, you must do so after the system requests
* a printer update. Otherwise, the printer will be ignored.
* </p>
* <p>
* During printer discovery all printers that are known to your print service
* have to be added. The system does not retain any printers from previous
* sessions.
* </p>
*/
public abstract class PrinterDiscoverySession {
private static final String LOG_TAG = "PrinterDiscoverySession";
private static int sIdCounter = 0;
private final Object mLock = new Object();
private final Handler mHandler;
private final int mId;
private IPrinterDiscoverySessionController mController;
private IPrinterDiscoverySessionObserver mObserver;
/**
* Constructor.
*
* @param context A context instance.
*/
public PrinterDiscoverySession(Context context) {
mId = sIdCounter++;
mHandler = new SessionHandler(context.getMainLooper());
mController = new PrinterDiscoverySessionController(this);
}
void setObserver(IPrinterDiscoverySessionObserver observer) {
synchronized (mLock) {
mObserver = observer;
try {
mObserver.setController(mController);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error setting session controller", re);
}
}
}
int getId() {
return mId;
}
/**
* Adds discovered printers. Adding an already added printer has no effect.
* Removed printers can be added again. You can call this method multiple
* times during printer discovery.
* <p>
* <strong>Note: </strong> Calling this method when the session is closed,
* which is if {@link #isClosed()} returns true, will throw an {@link
* IllegalStateException}.
* </p>
*
* @param printers The printers to add.
*
* @see #removePrinters(List)
* @see #updatePrinters(List)
* @see #isClosed()
*/
public final void addPrinters(List<PrinterInfo> printers) {
final IPrinterDiscoverySessionObserver observer;
synchronized (mLock) {
throwIfClosedLocked();
observer = mObserver;
}
if (observer != null) {
try {
observer.onPrintersAdded(printers);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error adding printers", re);
}
}
}
/**
* Removes added printers. Removing an already removed or never added
* printer has no effect. Removed printers can be added again. You
* can call this method multiple times during printer discovery.
* <p>
* <strong>Note: </strong> Calling this method when the session is closed,
* which is if {@link #isClosed()} returns true, will throw an {@link
* IllegalStateException}.
* </p>
*
* @param printerIds The ids of the removed printers.
*
* @see #addPrinters(List)
* @see #updatePrinters(List)
* @see #isClosed()
*/
public final void removePrinters(List<PrinterId> printerIds) {
final IPrinterDiscoverySessionObserver observer;
synchronized (mLock) {
throwIfClosedLocked();
observer = mObserver;
}
if (observer != null) {
try {
observer.onPrintersRemoved(printerIds);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error removing printers", re);
}
}
}
/**
* Updates added printers. Updating a printer that was not added or that
* was removed has no effect. You can call this method multiple times
* during printer discovery.
* <p>
* <strong>Note: </strong> Calling this method when the session is closed,
* which is if {@link #isClosed()} returns true, will throw an {@link
* IllegalStateException}.
* </p>
*
* @param printers The printers to update.
*
* @see #addPrinters(List)
* @see #removePrinters(List)
* @see #isClosed()
*/
public final void updatePrinters(List<PrinterInfo> printers) {
final IPrinterDiscoverySessionObserver observer;
synchronized (mLock) {
throwIfClosedLocked();
observer = mObserver;
}
if (observer != null) {
try {
observer.onPrintersUpdated(printers);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error updating printers", re);
}
}
}
/**
* Callback notifying you that the session is open and you should start
* printer discovery. Discovered printers should be added via calling
* {@link #addPrinters(List)}. Added printers that disappeared should be
* removed via calling {@link #removePrinters(List)}. Added printers whose
* properties or capabilities changes should be updated via calling {@link
* #updatePrinters(List)}. When the session is closed you will receive a
* call to {@link #onClose()}.
* <p>
* During printer discovery all printers that are known to your print
* service have to be added. The system does not retain any printers from
* previous sessions.
* </p>
* <p>
* <strong>Note: </strong>You are also given a list of printers whose
* availability has to be checked first. For example, these printers could
* be the user's favorite ones, therefore they have to be verified first.
* </p>
*
* @see #onClose()
* @see #isClosed()
* @see #addPrinters(List)
* @see #removePrinters(List)
* @see #updatePrinters(List)
*/
public abstract void onOpen(List<PrinterId> priorityList);
/**
* Callback notifying you that the session is closed and you should stop
* printer discovery. After the session is closed and any attempt to call
* any of its methods will throw an exception. Whether a session is closed
* can be checked by calling {@link #isClosed()}. Once the session is closed
* it will never be opened again.
*
* @see #onOpen(List)
* @see #isClosed()
* @see #addPrinters(List)
* @see #removePrinters(List)
* @see #updatePrinters(List)
*/
public abstract void onClose();
/**
* Requests that you update a printer. You are responsible for updating
* the printer by also reporting its capabilities via calling {@link
* #updatePrinters(List)}.
* <p>
* <strong>Note: </strong> A printer can be initially added without its
* capabilities to avoid polling printers that the user will not select.
* However, after this method is called you are expected to update the
* printer <strong>including</strong> its capabilities. Otherwise, the
* printer will be ignored.
* <p>
* A scenario when you may be requested to update a printer is if the user
* selects it and the system has to present print options UI based on the
* printer's capabilities.
* </p>
*
* @param printerId The printer id.
*
* @see #updatePrinters(List)
* @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo)
* PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo)
*/
public abstract void onRequestPrinterUpdate(PrinterId printerId);
/**
* Gets whether this session is closed.
*
* @return Whether the session is closed.
*/
public final boolean isClosed() {
synchronized (mLock) {
return (mController == null && mObserver == null);
}
}
void close() {
synchronized (mLock) {
throwIfClosedLocked();
mController = null;
mObserver = null;
}
}
private void throwIfClosedLocked() {
if (isClosed()) {
throw new IllegalStateException("Session is closed");
}
}
private final class SessionHandler extends Handler {
public static final int MSG_OPEN = 1;
public static final int MSG_CLOSE = 2;
public static final int MSG_REQUEST_PRINTER_UPDATE = 3;
public SessionHandler(Looper looper) {
super(looper, null, true);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
case MSG_OPEN: {
List<PrinterId> priorityList = (List<PrinterId>) message.obj;
onOpen(priorityList);
} break;
case MSG_CLOSE: {
onClose();
close();
} break;
case MSG_REQUEST_PRINTER_UPDATE: {
PrinterId printerId = (PrinterId) message.obj;
onRequestPrinterUpdate(printerId);
} break;
}
}
}
private static final class PrinterDiscoverySessionController extends
IPrinterDiscoverySessionController.Stub {
private final WeakReference<PrinterDiscoverySession> mWeakSession;
public PrinterDiscoverySessionController(PrinterDiscoverySession session) {
mWeakSession = new WeakReference<PrinterDiscoverySession>(session);
}
@Override
public void open(List<PrinterId> priorityList) {
PrinterDiscoverySession session = mWeakSession.get();
if (session != null) {
session.mHandler.obtainMessage(SessionHandler.MSG_OPEN,
priorityList).sendToTarget();
}
}
@Override
public void close() {
PrinterDiscoverySession session = mWeakSession.get();
if (session != null) {
session.mHandler.sendEmptyMessage(SessionHandler.MSG_CLOSE);
}
}
@Override
public void requestPrinterUpdate(PrinterId printerId) {
PrinterDiscoverySession session = mWeakSession.get();
if (session != null) {
session.mHandler.obtainMessage(
SessionHandler.MSG_REQUEST_PRINTER_UPDATE,
printerId).sendToTarget();
}
}
};
}

View File

@@ -0,0 +1,24 @@
<HTML>
<BODY>
<p>
Provides classes for implementing print services. Print services are plug-in components
that know how to talk to printers via some standard protocols. These services serve as a
bridge between the system and the printers. Hence, the printer and print protocol specific
implementation is factored out of the system and can by independently developed and updated.
</p>
<p>
A print service implementation should extend {@link android.printservice.PrintService}
and implement its abstract methods. Also the print service has to follow the contract for
managing print {@link android.printservice.PrintJob}s to ensure correct interaction with
the system and consistent user experience.
<p/>
<p>
The system is responsible for starting and stopping a print service depending on whether
there are active print jobs for the printers managed by the service. The print service
should also perform printer discovery in a timely fashion to ensure good user experience.
The interaction between the system and the print service during printer discovery is
encapsulated by a {@link android.printservice.PrinterDiscoverySession} instance created
by the print service when requested by the system.
</p>
</BODY>
</HTML>

View File

@@ -86,7 +86,7 @@ public class NotificationController {
printJob.getLabel()))
.addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
createCancelIntent(printJob))
.setContentText(printJob.getPrinterId().getPrinterName())
.setContentText(printJob.getPrinterName())
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
@@ -119,7 +119,7 @@ public class NotificationController {
intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + String.valueOf(printJob.getId()));
intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJob.getId());
intent.putExtra(INTENT_EXTRA_PRINTJOB_LABEL, printJob.getLabel());
intent.putExtra(INTENT_EXTRA_PRINTER_NAME, printJob.getPrinterId().getPrinterName());
intent.putExtra(INTENT_EXTRA_PRINTER_NAME, printJob.getPrinterName());
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}

View File

@@ -34,7 +34,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapter;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionController;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -42,6 +43,7 @@ import android.print.PrintAttributes.MediaSize;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.text.Editable;
@@ -70,6 +72,7 @@ import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -133,7 +136,7 @@ public class PrintJobConfigActivity extends Activity {
private Editor mEditor;
private Document mDocument;
private PrintController mController;
private PrinterDiscoveryObserver mPrinterDiscoveryObserver;
private PrinterDiscoverySessionObserver mPrinterDiscoverySessionObserver;
private int mPrintJobId;
@@ -181,8 +184,9 @@ public class PrintJobConfigActivity extends Activity {
mController.initialize();
mEditor.initialize();
mPrinterDiscoveryObserver = new PrinterDiscoveryObserver(mEditor, getMainLooper());
mSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
mPrinterDiscoverySessionObserver = new PrinterDiscoverySessionObserver(mEditor,
getMainLooper());
mSpooler.createPrinterDiscoverySession(mPrinterDiscoverySessionObserver);
}
@Override
@@ -190,9 +194,9 @@ public class PrintJobConfigActivity extends Activity {
// We can safely do the work in here since at this point
// the system is bound to our (spooler) process which
// guarantees that this process will not be killed.
mSpooler.stopPrinterDiscovery();
mPrinterDiscoveryObserver.destroy();
mPrinterDiscoveryObserver = null;
mPrinterDiscoverySessionObserver.close();
mPrinterDiscoverySessionObserver.destroy();
mPrinterDiscoverySessionObserver = null;
if (mController.hasStarted()) {
mController.finish();
}
@@ -622,14 +626,15 @@ public class PrintJobConfigActivity extends Activity {
SpinnerItem<PrinterInfo> dstItem = mDestinationSpinnerAdapter.getItem(position);
if (dstItem != null) {
PrinterInfo printer = dstItem.value;
mSpooler.setPrintJobPrinterIdNoPersistence(mPrintJobId, printer.getId());
printer.getDefaults(mCurrPrintAttributes);
if (!printer.hasAllRequiredAttributes()) {
mSpooler.setPrintJobPrinterNoPersistence(mPrintJobId, printer.getId());
PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
if (capabilities == null) {
List<PrinterId> printerIds = new ArrayList<PrinterId>();
printerIds.add(printer.getId());
mSpooler.onReqeustUpdatePrinters(printerIds);
mPrinterDiscoverySessionObserver.requestPrinterUpdate(printer.getId());
//TODO: We need a timeout for the update.
} else {
capabilities.getDefaults(mCurrPrintAttributes);
if (!mController.hasStarted()) {
mController.start();
}
@@ -1058,8 +1063,8 @@ public class PrintJobConfigActivity extends Activity {
final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
if (selectedIndex < 0 || !mDestinationSpinnerAdapter.getItem(
selectedIndex).value.hasAllRequiredAttributes()) {
if (selectedIndex < 0 || mDestinationSpinnerAdapter.getItem(
selectedIndex).value.getCapabilities() == null) {
// Destination
mDestinationSpinner.setEnabled(false);
@@ -1117,7 +1122,8 @@ public class PrintJobConfigActivity extends Activity {
} else {
PrintAttributes defaultAttributes = mTempPrintAttributes;
PrinterInfo printer = mDestinationSpinnerAdapter.getItem(selectedIndex).value;
printer.getDefaults(defaultAttributes);
PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
printer.getCapabilities().getDefaults(defaultAttributes);
// Destination
if (mDestinationSpinnerAdapter.getCount() > 1) {
@@ -1130,7 +1136,7 @@ public class PrintJobConfigActivity extends Activity {
mCopiesEditText.setEnabled(true);
// Media size.
List<MediaSize> mediaSizes = printer.getMediaSizes();
List<MediaSize> mediaSizes = capabilities.getMediaSizes();
boolean mediaSizesChanged = false;
final int mediaSizeCount = mediaSizes.size();
if (mediaSizeCount != mMediaSizeSpinnerAdapter.getCount()) {
@@ -1168,7 +1174,7 @@ public class PrintJobConfigActivity extends Activity {
}
// Color mode.
final int colorModes = printer.getColorModes();
final int colorModes = capabilities.getColorModes();
boolean colorModesChanged = false;
if (Integer.bitCount(colorModes) != mColorModeSpinnerAdapter.getCount()) {
colorModesChanged = true;
@@ -1219,7 +1225,7 @@ public class PrintJobConfigActivity extends Activity {
}
// Orientation.
final int orientations = printer.getOrientations();
final int orientations = capabilities.getOrientations();
boolean orientationsChanged = false;
if (Integer.bitCount(orientations) != mOrientationSpinnerAdapter.getCount()) {
orientationsChanged = true;
@@ -1345,7 +1351,7 @@ public class PrintJobConfigActivity extends Activity {
}
if (!duplicate) {
mDestinationSpinnerAdapter.add(new SpinnerItem<PrinterInfo>(
addedPrinter, addedPrinter.getId().getPrinterName()));
addedPrinter, addedPrinter.getName()));
} else {
Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter);
}
@@ -1470,7 +1476,7 @@ public class PrintJobConfigActivity extends Activity {
PrinterInfo printerInfo = getItem(position).value;
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(printerInfo.getId().getPrinterName());
title.setText(printerInfo.getName());
try {
TextView subtitle = (TextView)
@@ -1489,21 +1495,35 @@ public class PrintJobConfigActivity extends Activity {
}
}
private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
private static final int MSG_ON_PRINTERS_ADDED = 1;
private static final int MSG_ON_PRINTERS_REMOVED = 2;
private static final int MSG_ON_PRINTERS_UPDATED = 3;
private static final class PrinterDiscoverySessionObserver
extends IPrinterDiscoverySessionObserver.Stub {
private static final int MSG_SET_CONTROLLER = 1;
private static final int MSG_ON_PRINTERS_ADDED = 2;
private static final int MSG_ON_PRINTERS_REMOVED = 3;
private static final int MSG_ON_PRINTERS_UPDATED = 4;
private Handler mHandler;
private Editor mEditor;
private IPrinterDiscoverySessionController mController;
@SuppressWarnings("unchecked")
public PrinterDiscoveryObserver(Editor editor, Looper looper) {
public PrinterDiscoverySessionObserver(Editor editor, Looper looper) {
mEditor = editor;
mHandler = new Handler(looper, null, true) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_SET_CONTROLLER: {
mController = (IPrinterDiscoverySessionController) message.obj;
// TODO: This should be cleaned up
List<PrinterId> printerIds = Collections.emptyList();
try {
mController.open(printerIds);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error starting printer discovery");
}
} break;
case MSG_ON_PRINTERS_ADDED: {
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
mEditor.addPrinters(printers);
@@ -1523,6 +1543,46 @@ public class PrintJobConfigActivity extends Activity {
};
}
public void open(List<PrinterId> priorityList) {
if (mController != null) {
try {
mController.open(priorityList);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error closing printer discovery session", re);
}
}
}
public void close() {
if (mController != null) {
try {
mController.close();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error closing printer discovery session", re);
}
}
}
public void requestPrinterUpdate(PrinterId printerId) {
if (mController != null) {
try {
mController.requestPrinterUpdate(printerId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error requestin printer update", re);
}
}
}
@Override
public void setController(IPrinterDiscoverySessionController controller) {
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(MSG_SET_CONTROLLER, controller)
.sendToTarget();
}
}
}
@Override
public void onPrintersAdded(List<PrinterInfo> printers) {
synchronized (this) {

View File

@@ -21,7 +21,7 @@ import android.content.Context;
import android.os.AsyncTask;
import android.os.ParcelFileDescriptor;
import android.print.IPrintClient;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
@@ -32,6 +32,7 @@ import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -199,16 +200,8 @@ public class PrintSpooler {
}
}
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
mService.startPrinterDiscovery(observer);
}
public void stopPrinterDiscovery() {
mService.stopPrinterDiscovery();
}
public void onReqeustUpdatePrinters(List<PrinterId> printerIds) {
mService.onReqeustUpdatePrinters(printerIds);
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
mService.createPrinterDiscoverySession(observer);
}
private int generatePrintJobIdLocked() {
@@ -417,11 +410,12 @@ public class PrintSpooler {
}
}
public void setPrintJobPrinterIdNoPersistence(int printJobId, PrinterId printerId) {
public void setPrintJobPrinterNoPersistence(int printJobId, PrinterInfo printer) {
synchronized (mLock) {
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
printJob.setPrinterId(printerId);
printJob.setPrinterId(printer.getId());
printJob.setPrinterName(printer.getName());
}
}
}
@@ -500,6 +494,7 @@ public class PrintSpooler {
private static final String ATTR_START = "start";
private static final String ATTR_END = "end";
private static final String ATTR_NAME = "name";
private static final String ATTR_PAGE_COUNT = "pageCount";
private static final String ATTR_CONTENT_TYPE = "contentType";
@@ -573,7 +568,7 @@ public class PrintSpooler {
PrinterId printerId = printJob.getPrinterId();
if (printerId != null) {
serializer.startTag(null, TAG_PRINTER_ID);
serializer.attribute(null, ATTR_PRINTER_NAME, printerId.getPrinterName());
serializer.attribute(null, ATTR_PRINTER_NAME, printerId.getLocalId());
serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
.flattenToString());
serializer.endTag(null, TAG_PRINTER_ID);
@@ -675,6 +670,7 @@ public class PrintSpooler {
PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
if (documentInfo != null) {
serializer.startTag(null, TAG_DOCUMENT_INFO);
serializer.attribute(null, ATTR_NAME, documentInfo.getName());
serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
documentInfo.getContentType()));
serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
@@ -921,11 +917,13 @@ public class PrintSpooler {
skipEmptyTextTags(parser);
if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
ATTR_PAGE_COUNT));
final int contentType = Integer.parseInt(parser.getAttributeValue(null,
ATTR_CONTENT_TYPE));
PrintDocumentInfo info = new PrintDocumentInfo.Builder().setPageCount(pageCount)
PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
.setPageCount(pageCount)
.setContentType(contentType).create();
printJob.setDocumentInfo(info);
parser.next();

View File

@@ -32,10 +32,9 @@ import android.print.IPrintDocumentAdapter;
import android.print.IPrintSpooler;
import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.util.Log;
import android.util.Slog;
@@ -167,20 +166,11 @@ public final class PrintSpoolerService extends Service {
printJob).sendToTarget();
}
public void onReqeustUpdatePrinters(List<PrinterId> printers) {
mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
printers).sendToTarget();
}
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
mHandler.obtainMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
observer).sendToTarget();
}
public void stopPrinterDiscovery() {
mHandler.sendEmptyMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY);
}
public void onAllPrintJobsForServiceHandled(ComponentName service) {
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
service).sendToTarget();
@@ -193,12 +183,10 @@ public final class PrintSpoolerService extends Service {
private final class MyHandler extends Handler {
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_START_PRINTER_DISCOVERY = 3;
public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 4;
public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 3;
public static final int MSG_ON_PRINT_JOB_QUEUED = 5;
public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 6;
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 7;
public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 8;
public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 9;
public MyHandler(Looper looper) {
@@ -206,7 +194,6 @@ public final class PrintSpoolerService extends Service {
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
case MSG_SET_CLIENT: {
@@ -233,23 +220,14 @@ public final class PrintSpoolerService extends Service {
}
} break;
case MSG_ON_START_PRINTER_DISCOVERY: {
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) message.obj;
case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
IPrinterDiscoverySessionObserver observer =
(IPrinterDiscoverySessionObserver) message.obj;
if (mClient != null) {
try {
mClient.onStartPrinterDiscovery(observer);
mClient.createPrinterDiscoverySession(observer);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
}
}
} break;
case MSG_ON_STOP_PRINTER_DISCOVERY: {
if (mClient != null) {
try {
mClient.onStopPrinterDiscovery();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
Log.e(LOG_TAG, "Error creating printer discovery session.", re);
}
}
} break;
@@ -287,17 +265,6 @@ public final class PrintSpoolerService extends Service {
}
} break;
case MSG_ON_REQUEST_UPDATE_PRINTERS: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
if (mClient != null) {
try {
mClient.onRequestUpdatePrinters(printerIds);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting to update pritners.", re);
}
}
} break;
case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
PrintSpooler spooler = PrintSpooler.peekInstance();
if (spooler != null) {

View File

@@ -24,13 +24,14 @@ import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.IBinder.DeathRecipient;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionController;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.print.PrinterId;
@@ -128,18 +129,18 @@ final class RemotePrintService implements DeathRecipient {
printJob).sendToTarget();
}
private void handleOnRequestCancelPrintJob(final PrintJobInfo printJob) {
private void handleRequestCancelPrintJob(final PrintJobInfo printJob) {
throwIfDestroyed();
// If we are not bound, then we have no print jobs to handle
// which means that there are no print jobs to be cancelled.
if (isBound()) {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnRequestCancelPrintJob()");
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleRequestCancelPrintJob()");
}
try {
mPrintService.onRequestCancelPrintJob(printJob);
mPrintService.requestCancelPrintJob(printJob);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error canceling pring job.", re);
Slog.e(LOG_TAG, "Error canceling a pring job.", re);
}
}
}
@@ -171,85 +172,34 @@ final class RemotePrintService implements DeathRecipient {
}
}
public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY, observer).sendToTarget();
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
mHandler.obtainMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
observer).sendToTarget();
}
private void handleOnStartPrinterDiscovery(final IPrinterDiscoveryObserver observer) {
private void handleCreatePrinterDiscoverySession(
final IPrinterDiscoverySessionObserver observer) {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleOnStartPrinterDiscovery(observer);
handleCreatePrinterDiscoverySession(observer);
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] onStartPrinterDiscovery()");
Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()");
}
try {
mPrintService.onStartPrinterDiscovery(observer);
mPrintService.createPrinterDiscoverySession(observer);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error announcing start printer dicovery.", re);
}
}
}
public void onStopPrinterDiscovery() {
mHandler.sendEmptyMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY);
}
private void handleStopPrinterDiscovery() {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleStopPrinterDiscovery();
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] onStopPrinterDiscovery()");
}
try {
mPrintService.onStopPrinterDiscovery();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error announcing stop printer dicovery.", re);
}
}
}
public void onRequestUpdatePrinters(List<PrinterId> printerIds) {
mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
printerIds).sendToTarget();
}
private void handleReqeustUpdatePrinters(final List<PrinterId> printerIds) {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleReqeustUpdatePrinters(printerIds);
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleReqeustUpdatePrinters()");
}
try {
mPrintService.onRequestUpdatePrinters(printerIds);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting to update printers.", re);
}
}
}
private boolean isBound() {
return mPrintService != null;
}
@@ -331,18 +281,15 @@ final class RemotePrintService implements DeathRecipient {
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 1;
public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 2;
public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
public static final int MSG_ON_START_PRINTER_DISCOVERY = 4;
public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 5;
public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 6;
public static final int MSG_DESTROY = 7;
public static final int MSG_BINDER_DIED = 8;
public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 4;
public static final int MSG_DESTROY = 6;
public static final int MSG_BINDER_DIED = 7;
public MyHandler(Looper looper) {
super(looper, null, false);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
@@ -351,7 +298,7 @@ final class RemotePrintService implements DeathRecipient {
case MSG_ON_REQUEST_CANCEL_PRINT_JOB: {
PrintJobInfo printJob = (PrintJobInfo) message.obj;
handleOnRequestCancelPrintJob(printJob);
handleRequestCancelPrintJob(printJob);
} break;
case MSG_ON_PRINT_JOB_QUEUED: {
@@ -359,21 +306,13 @@ final class RemotePrintService implements DeathRecipient {
handleOnPrintJobQueued(printJob);
} break;
case MSG_ON_START_PRINTER_DISCOVERY: {
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) message.obj;
handleOnStartPrinterDiscovery(new SecurePrinterDiscoveryObserver(
case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
IPrinterDiscoverySessionObserver observer =
(IPrinterDiscoverySessionObserver) message.obj;
handleCreatePrinterDiscoverySession(new SecurePrinterDiscoverySessionObserver(
mComponentName, observer));
} break;
case MSG_ON_REQUEST_UPDATE_PRINTERS: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
handleReqeustUpdatePrinters(printerIds);
} break;
case MSG_ON_STOP_PRINTER_DISCOVERY: {
handleStopPrinterDiscovery();
} break;
case MSG_DESTROY: {
handleDestroy();
} break;
@@ -464,14 +403,14 @@ final class RemotePrintService implements DeathRecipient {
}
}
private static final class SecurePrinterDiscoveryObserver
extends IPrinterDiscoveryObserver.Stub {
private static final class SecurePrinterDiscoverySessionObserver
extends IPrinterDiscoverySessionObserver.Stub {
private final ComponentName mComponentName;
private final IPrinterDiscoveryObserver mDecoratedObsever;
private final IPrinterDiscoverySessionObserver mDecoratedObsever;
public SecurePrinterDiscoveryObserver(ComponentName componentName,
IPrinterDiscoveryObserver observer) {
public SecurePrinterDiscoverySessionObserver(ComponentName componentName,
IPrinterDiscoverySessionObserver observer) {
mComponentName = componentName;
mDecoratedObsever = observer;
}
@@ -506,6 +445,15 @@ final class RemotePrintService implements DeathRecipient {
}
}
@Override
public void setController(IPrinterDiscoverySessionController controller) {
try {
mDecoratedObsever.setController(controller);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error setting controller", re);
}
}
private void throwIfPrinterIdsForPrinterInfoTampered(
List<PrinterInfo> printerInfos) {
final int printerInfoCount = printerInfos.size();

View File

@@ -32,10 +32,9 @@ import android.print.IPrintDocumentAdapter;
import android.print.IPrintSpooler;
import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.util.Slog;
import android.util.TimedRemoteCaller;
@@ -92,10 +91,8 @@ final class RemotePrintSpooler {
public static interface PrintSpoolerCallbacks {
public void onPrintJobQueued(PrintJobInfo printJob);
public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
public void onStopPrinterDiscovery();
public void onAllPrintJobsForServiceHandled(ComponentName printService);
public void onRequestUpdatePrinters(List<PrinterId> printerIds);
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
}
public RemotePrintSpooler(Context context, int userId,
@@ -600,38 +597,12 @@ final class RemotePrintSpooler {
}
@Override
public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
RemotePrintSpooler spooler = mWeakSpooler.get();
if (spooler != null) {
final long identity = Binder.clearCallingIdentity();
try {
spooler.mCallbacks.onStartPrinterDiscovery(observer);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void onStopPrinterDiscovery() {
RemotePrintSpooler spooler = mWeakSpooler.get();
if (spooler != null) {
final long identity = Binder.clearCallingIdentity();
try {
spooler.mCallbacks.onStopPrinterDiscovery();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void onRequestUpdatePrinters(List<PrinterId> printerIds) {
RemotePrintSpooler spooler = mWeakSpooler.get();
if (spooler != null) {
final long identity = Binder.clearCallingIdentity();
try {
spooler.mCallbacks.onRequestUpdatePrinters(printerIds);
spooler.mCallbacks.createPrinterDiscoverySession(observer);
} finally {
Binder.restoreCallingIdentity(identity);
}

View File

@@ -21,9 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
@@ -106,7 +105,7 @@ final class UserState implements PrintSpoolerCallbacks {
}
@Override
public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
final List<RemotePrintService> services;
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -118,39 +117,7 @@ final class UserState implements PrintSpoolerCallbacks {
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
service.onStartPrinterDiscovery(observer);
}
}
@Override
public void onStopPrinterDiscovery() {
final List<RemotePrintService> services;
synchronized (mLock) {
throwIfDestroyedLocked();
if (mActiveServices.isEmpty()) {
return;
}
services = new ArrayList<RemotePrintService>(mActiveServices.values());
}
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
service.onStopPrinterDiscovery();
}
}
@Override
public void onRequestUpdatePrinters(List<PrinterId> printerIds) {
final RemotePrintService service;
synchronized (mLock) {
throwIfDestroyedLocked();
if (mActiveServices.isEmpty()) {
return;
}
service = mActiveServices.get(printerIds.get(0).getServiceName());
}
if (service != null) {
service.onRequestUpdatePrinters(printerIds);
service.createPrinterDiscoverySession(observer);
}
}