diff options
Diffstat (limited to 'core/java')
18 files changed, 1819 insertions, 1109 deletions
diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl index 46857e4..8db2169 100644 --- a/core/java/android/print/IPrintSpoolerClient.aidl +++ b/core/java/android/print/IPrintSpoolerClient.aidl @@ -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(); } diff --git a/core/java/android/print/IPrinterDiscoveryObserver.aidl b/core/java/android/print/IPrinterDiscoverySessionController.aidl index deabbcb..13116ef 100644 --- a/core/java/android/print/IPrinterDiscoveryObserver.aidl +++ b/core/java/android/print/IPrinterDiscoverySessionController.aidl @@ -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(); } diff --git a/core/java/android/print/IPrinterDiscoverySessionObserver.aidl b/core/java/android/print/IPrinterDiscoverySessionObserver.aidl new file mode 100644 index 0000000..a78924c --- /dev/null +++ b/core/java/android/print/IPrinterDiscoverySessionObserver.aidl @@ -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); +} diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index 911e380..a902c72 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -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"; diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index 29e8e7c..653ad4b 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -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,11 +74,21 @@ 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. * * @return The 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. diff --git a/core/java/android/print/FileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java index d642a61..4503eda 100644 --- a/core/java/android/print/FileDocumentAdapter.java +++ b/core/java/android/print/PrintFileDocumentAdapter.java @@ -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 diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 096dcd5..2fb4751 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -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(); @@ -245,6 +250,28 @@ public final class PrintJobInfo implements Parcelable { } /** + * 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. * * @return The 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); diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index c067661..636b9d4 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -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); } diff --git a/core/java/android/print/PrinterCapabilitiesInfo.aidl b/core/java/android/print/PrinterCapabilitiesInfo.aidl new file mode 100644 index 0000000..0f5fb6b --- /dev/null +++ b/core/java/android/print/PrinterCapabilitiesInfo.aidl @@ -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; diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java new file mode 100644 index 0000000..70b418c --- /dev/null +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -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]; + } + }; +} + diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java index 8462736..a3f3b2b 100644 --- a/core/java/android/print/PrinterId.java +++ b/core/java/android/print/PrinterId.java @@ -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(); } diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index 10cecca..ac782a8 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -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 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 mName; - private int mDuplexModes; - private int mColorModes; - private int mFittingModes; - private int mOrientations; + private int mStatus; - private final int[] mDefaults = new int[PROPERTY_COUNT]; - private Margins mDefaultMargins = DEFAULT_MARGINS; + private String mDescription; - /** - * @hide - */ - public PrinterInfo() { - Arrays.fill(mDefaults, DEFAULT_UNDEFINED); + private PrinterCapabilitiesInfo mCapabilities; + + 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,193 +77,47 @@ public final class PrinterInfo implements Parcelable { } /** - * Gets the status of the printer. - * - * @return The status. - */ - public int getStatus() { - return mStatus; - } - - /** - * Gets the supported media sizes. - * - * @return The supported media sizes. - */ - public List<MediaSize> getMediaSizes() { - return mMediaSizes; - } - - /** - * Gets the supported resolutions. - * - * @return The supported 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. + * Get the printer name. * - * @see PrintAttributes#DUPLEX_MODE_NONE - * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE - * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE + * @return The printer name. */ - public int getDuplexModes() { - return mDuplexModes; + public String getName() { + return mName; } /** - * Gets the supported color modes. + * Gets the printer status. * - * @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 + * @return The status. */ - public int getOrientations() { - return mOrientations; + public int getStatus() { + return mStatus; } /** - * Gets the default print attributes. + * Gets the printer description. * - * @param outAttributes The attributes to populated. + * @return The description. */ - 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); - } + public String getDescription() { + return mDescription; } /** - * 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. + * Gets the printer capabilities. * - * @return Whether this info has all required attributes. + * @return The capabilities. */ - 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 (mStatus != other.mStatus) { - return false; - } - 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) { + if (!TextUtils.equals(mName, other.mName)) { return false; } - if (mFittingModes != other.mFittingModes) { - return false; - } - if (mOrientations != other.mOrientations) { + if (mStatus != other.mStatus) { return false; } - if (!Arrays.equals(mDefaults, other.mDefaults)) { + if (!TextUtils.equals(mDescription, other.mDescription)) { return false; } - if (mDefaultMargins == null) { - if (other.mDefaultMargins != null) { + if (mCapabilities == null) { + if (other.mCapabilities != 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; - return this; - } - - /** - * 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. + * Constructor. * - * @see PrintAttributes#COLOR_MODE_COLOR - * @see PrintAttributes#COLOR_MODE_MONOCHROME + * @param prototype Prototype from which to start building. */ - 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; + public Builder(PrinterInfo prototype) { + mPrototype = prototype; } /** - * Set the duplex modes. - * <p> - * <strong>Required:</strong> No - * </p> + * Sets the printer name. * - * @param duplexModes The duplex mode bit mask. - * @param defaultDuplexMode The default duplex mode. + * @param name The name. * @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; + public Builder setName(String name) { + mPrototype.mName = name; return this; } /** - * Sets the fitting modes. - * <p> - * <strong>Required:</strong> No - * </p> + * Sets the printer description. * - * @param fittingModes The fitting mode bit mask. - * @param defaultFittingMode The default fitting mode. + * @param description The description. * @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; + public Builder setDescription(String description) { + mPrototype.mDescription = description; return this; } /** - * Sets the orientations. - * <p> - * <strong>Required:</strong> Yes - * </p> + * Sets the printer capabilities. * - * @param orientations The orientation bit mask. - * @param defaultOrientation The default orientation. + * @param capabilities The capabilities. * @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); } } diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl index e6fdbf9..16b7a26 100644 --- a/core/java/android/printservice/IPrintService.aidl +++ b/core/java/android/printservice/IPrintService.aidl @@ -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ÿ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); } diff --git a/core/java/android/printservice/PrintDocument.java b/core/java/android/printservice/PrintDocument.java index 2a1581a..7437dc5 100644 --- a/core/java/android/printservice/PrintDocument.java +++ b/core/java/android/printservice/PrintDocument.java @@ -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; diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index 64c079e..5f9a730 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -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; @@ -112,6 +114,42 @@ public final class PrintJob { } /** + * 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()) { diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 49384db..92bccd4 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -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> * <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> <service android:name=".MyPrintService" * android:permission="android.permission.BIND_PRINT_SERVICE"> * <intent-filter> @@ -147,8 +142,9 @@ import java.util.List; * </service></pre> * </p> * <p> - * For more details refer to {@link #SERVICE_META_DATA} and - * <code><{@link android.R.styleable#PrintService print-service}></code>. + * For more details for how to configure your print service via the meta-data refer to + * {@link #SERVICE_META_DATA} and <code><{@link android.R.styleable#PrintService + * print-service}></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><{@link android.R.styleable#PrintService print-service}></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> + * <{@link android.R.styleable#PrintService print-service}></code> tag. This is + * a sample XML file configuring a print service: * <pre> <print-service * android:vendor="SomeVendor" * android:settingsActivity="foo.bar.MySettingsActivity" * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity." * . . . * /></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)}. - * - * @see #onStopPrinterDiscovery() - * @see #addDiscoveredPrinters(List) - * @see #removeDiscoveredPrinters(List) - * @see #updateDiscoveredPrinters(List) - */ - protected abstract void onStartPrinterDiscovery(); - - /** - * 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> + * Callback asking you to create a new {@link PrinterDiscoverySession}. * - * @param printerIds A list with disappeared printer ids. - * - * @see #addDiscoveredPrinters(List) - * @see #updateDiscoveredPrinters(List) - * @see #onStartPrinterDiscovery() - * @see #onStopPrinterDiscovery() + * @see PrinterDiscoverySession */ - 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); - } - } - } + protected abstract PrinterDiscoverySession onCreatePrinterDiscoverySession(); /** - * 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(); diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java new file mode 100644 index 0000000..5bc0f2e --- /dev/null +++ b/core/java/android/printservice/PrinterDiscoverySession.java @@ -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(); + } + } + }; +} diff --git a/core/java/android/printservice/package.html b/core/java/android/printservice/package.html new file mode 100644 index 0000000..6b0327c --- /dev/null +++ b/core/java/android/printservice/package.html @@ -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> |