Merge "Iteration on the print sub-system."
This commit is contained in:
committed by
Android (Google) Code Review
commit
9f1ef00265
@@ -18542,7 +18542,6 @@ package android.print {
|
||||
method public void clear();
|
||||
method public int describeContents();
|
||||
method public int getColorMode();
|
||||
method public int getCopies();
|
||||
method public int getDuplexMode();
|
||||
method public int getFittingMode();
|
||||
method public android.print.PrintAttributes.Tray getInputTray();
|
||||
@@ -18568,7 +18567,6 @@ package android.print {
|
||||
ctor public PrintAttributes.Builder();
|
||||
method public android.print.PrintAttributes create();
|
||||
method public android.print.PrintAttributes.Builder setColorMode(int);
|
||||
method public android.print.PrintAttributes.Builder setCopyCount(int);
|
||||
method public android.print.PrintAttributes.Builder setDuplexMode(int);
|
||||
method public android.print.PrintAttributes.Builder setFittingMode(int);
|
||||
method public android.print.PrintAttributes.Builder setInputTray(android.print.PrintAttributes.Tray);
|
||||
@@ -18654,18 +18652,20 @@ package android.print {
|
||||
method public void onFinish();
|
||||
method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
|
||||
method public void onStart();
|
||||
method public abstract void onWrite(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
|
||||
method public abstract void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
|
||||
field public static final java.lang.String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
|
||||
}
|
||||
|
||||
public static abstract class PrintDocumentAdapter.LayoutResultCallback {
|
||||
method public void onLayoutCancelled();
|
||||
method public void onLayoutFailed(java.lang.CharSequence);
|
||||
method public void onLayoutFinished(android.print.PrintDocumentInfo, boolean);
|
||||
}
|
||||
|
||||
public static abstract class PrintDocumentAdapter.WriteResultCallback {
|
||||
method public void onWriteCancelled();
|
||||
method public void onWriteFailed(java.lang.CharSequence);
|
||||
method public void onWriteFinished(java.util.List<android.print.PageRange>);
|
||||
method public void onWriteFinished(android.print.PageRange[]);
|
||||
}
|
||||
|
||||
public final class PrintDocumentInfo implements android.os.Parcelable {
|
||||
@@ -18696,6 +18696,7 @@ package android.print {
|
||||
public final class PrintJobInfo implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public android.print.PrintAttributes getAttributes();
|
||||
method public int getCopies();
|
||||
method public int getId();
|
||||
method public java.lang.CharSequence getLabel();
|
||||
method public android.print.PageRange[] getPages();
|
||||
|
||||
@@ -34,8 +34,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adapter for printing files.
|
||||
@@ -69,7 +67,7 @@ final class FileDocumentAdapter extends PrintDocumentAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWrite(List<PageRange> pages, FileDescriptor destination,
|
||||
public void onWrite(PageRange[] pages, FileDescriptor destination,
|
||||
CancellationSignal cancellationSignal, WriteResultCallback callback) {
|
||||
mWriteFileAsyncTask = new WriteFileAsyncTask(destination, cancellationSignal, callback);
|
||||
mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
@@ -127,9 +125,7 @@ final class FileDocumentAdapter extends PrintDocumentAdapter {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
List<PageRange> pages = new ArrayList<PageRange>();
|
||||
pages.add(PageRange.ALL_PAGES);
|
||||
mResultCallback.onWriteFinished(pages);
|
||||
mResultCallback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package android.print;
|
||||
|
||||
import android.os.ICancellationSignal;
|
||||
import android.print.PrintDocumentInfo;
|
||||
|
||||
/**
|
||||
@@ -25,7 +24,6 @@ import android.print.PrintDocumentInfo;
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ILayoutResultCallback {
|
||||
void onLayoutStarted(ICancellationSignal cancellationSignal);
|
||||
void onLayoutFinished(in PrintDocumentInfo info, boolean changed);
|
||||
void onLayoutFailed(CharSequence error);
|
||||
void onLayoutFinished(in PrintDocumentInfo info, boolean changed, int sequence);
|
||||
void onLayoutFailed(CharSequence error, int sequence);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ import android.print.PrintAttributes;
|
||||
oneway interface IPrintDocumentAdapter {
|
||||
void start();
|
||||
void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes,
|
||||
ILayoutResultCallback callback, in Bundle metadata);
|
||||
void write(in List<PageRange> pages, in ParcelFileDescriptor fd,
|
||||
IWriteResultCallback callback);
|
||||
ILayoutResultCallback callback, in Bundle metadata, int sequence);
|
||||
void write(in PageRange[] pages, in ParcelFileDescriptor fd,
|
||||
IWriteResultCallback callback, int sequence);
|
||||
void finish();
|
||||
}
|
||||
|
||||
@@ -33,5 +33,4 @@ interface IPrintManager {
|
||||
in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
|
||||
int appId, int userId);
|
||||
void cancelPrintJob(int printJobId, int appId, int userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ import java.util.List;
|
||||
*/
|
||||
oneway interface IPrintSpoolerCallbacks {
|
||||
void onGetPrintJobInfosResult(in List<PrintJobInfo> printJob, int sequence);
|
||||
void onGetPrintJobInfoResult(in PrintJobInfo printJob, int sequence);
|
||||
void onCreatePrintJobResult(in PrintJobInfo printJob, int sequence);
|
||||
void onCancelPrintJobResult(boolean canceled, int sequence);
|
||||
void onSetPrintJobStateResult(boolean success, int sequence);
|
||||
void onSetPrintJobTagResult(boolean success, int sequence);
|
||||
void onGetPrintJobInfoResult(in PrintJobInfo printJob, int sequence);
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.PrinterId;
|
||||
import android.print.PrinterInfo;
|
||||
|
||||
/**
|
||||
* Interface for observing the state of the print spooler.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IPrinterDiscoveryObserver {
|
||||
void onPrintJobQueued(in PrinterId printerId, in PrintJobInfo printJob);
|
||||
void onAllPrintJobsHandled(in ComponentName printService);
|
||||
void onAllPrintJobsHandled();
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package android.print;
|
||||
|
||||
import android.os.ICancellationSignal;
|
||||
import android.print.PageRange;
|
||||
|
||||
/**
|
||||
@@ -25,7 +24,6 @@ import android.print.PageRange;
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IWriteResultCallback {
|
||||
void onWriteStarted(ICancellationSignal cancellationSignal);
|
||||
void onWriteFinished(in List<PageRange> pages);
|
||||
void onWriteFailed(CharSequence error);
|
||||
void onWriteFinished(in PageRange[] pages, int sequence);
|
||||
void onWriteFailed(CharSequence error, int sequence);
|
||||
}
|
||||
|
||||
@@ -92,9 +92,39 @@ public final class PageRange implements Parcelable {
|
||||
parcel.writeInt(mEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + mEnd;
|
||||
result = prime * result + mStart;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PageRange other = (PageRange) obj;
|
||||
if (mEnd != other.mEnd) {
|
||||
return false;
|
||||
}
|
||||
if (mStart != other.mStart) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == ALL_PAGES) {
|
||||
if (mStart == 0 && mEnd == Integer.MAX_VALUE) {
|
||||
return "PageRange[<all pages>]";
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -77,7 +77,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
private int mColorMode;
|
||||
private int mFittingMode;
|
||||
private int mOrientation;
|
||||
private int mCopies;
|
||||
|
||||
PrintAttributes() {
|
||||
/* hide constructor */
|
||||
@@ -93,7 +92,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
mColorMode = parcel.readInt();
|
||||
mFittingMode = parcel.readInt();
|
||||
mOrientation = parcel.readInt();
|
||||
mCopies = parcel.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,29 +300,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
mOrientation = orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of copies.
|
||||
*
|
||||
* @return The number of copies or zero if not set.
|
||||
*/
|
||||
public int getCopies() {
|
||||
return mCopies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of copies.
|
||||
*
|
||||
* @param copyCount The number of copies.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setCopies(int copyCount) {
|
||||
if (copyCount < 1) {
|
||||
throw new IllegalArgumentException("Copies must be more than one.");
|
||||
}
|
||||
mCopies = copyCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
if (mMediaSize != null) {
|
||||
@@ -361,7 +336,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
parcel.writeInt(mColorMode);
|
||||
parcel.writeInt(mFittingMode);
|
||||
parcel.writeInt(mOrientation);
|
||||
parcel.writeInt(mCopies);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -369,6 +343,101 @@ public final class PrintAttributes implements Parcelable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + mColorMode;
|
||||
result = prime * result + mDuplexMode;
|
||||
result = prime * result + mFittingMode;
|
||||
result = prime * result + mOrientation;
|
||||
result = prime * result + ((mInputTray == null) ? 0 : mInputTray.hashCode());
|
||||
result = prime * result + ((mMargins == null) ? 0 : mMargins.hashCode());
|
||||
result = prime * result + ((mMediaSize == null) ? 0 : mMediaSize.hashCode());
|
||||
result = prime * result + ((mOutputTray == null) ? 0 : mOutputTray.hashCode());
|
||||
result = prime * result + ((mResolution == null) ? 0 : mResolution.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;
|
||||
}
|
||||
PrintAttributes other = (PrintAttributes) obj;
|
||||
if (mColorMode != other.mColorMode) {
|
||||
return false;
|
||||
}
|
||||
if (mDuplexMode != other.mDuplexMode) {
|
||||
return false;
|
||||
}
|
||||
if (mFittingMode != other.mFittingMode) {
|
||||
return false;
|
||||
}
|
||||
if (mOrientation != other.mOrientation) {
|
||||
return false;
|
||||
}
|
||||
if (mInputTray == null) {
|
||||
if (other.mInputTray != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!mInputTray.equals(other.mInputTray)) {
|
||||
return false;
|
||||
}
|
||||
if (mOutputTray == null) {
|
||||
if (other.mOutputTray != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!mOutputTray.equals(other.mOutputTray)) {
|
||||
return false;
|
||||
}
|
||||
if (mMargins == null) {
|
||||
if (other.mMargins != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!mMargins.equals(other.mMargins)) {
|
||||
return false;
|
||||
}
|
||||
if (mMediaSize == null) {
|
||||
if (other.mMediaSize != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!mMediaSize.equals(other.mMediaSize)) {
|
||||
return false;
|
||||
}
|
||||
if (mResolution == null) {
|
||||
if (other.mResolution != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!mResolution.equals(other.mResolution)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("PrintAttributes{");
|
||||
builder.append("mediaSize: ").append(mMediaSize);
|
||||
builder.append(", resolution: ").append(mResolution);
|
||||
builder.append(", margins: ").append(mMargins);
|
||||
builder.append(", inputTray: ").append(mInputTray);
|
||||
builder.append(", outputTray: ").append(mOutputTray);
|
||||
builder.append(", colorMode: ").append(colorModeToString(mColorMode));
|
||||
builder.append(", duplexMode: ").append(duplexModeToString(mDuplexMode));
|
||||
builder.append(", fittingMode: ").append(fittingModeToString(mFittingMode));
|
||||
builder.append(", orientation: ").append(orientationToString(mOrientation));
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/** hide */
|
||||
public void clear() {
|
||||
mMediaSize = null;
|
||||
@@ -380,7 +449,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
mColorMode = 0;
|
||||
mFittingMode = 0;
|
||||
mOrientation = 0;
|
||||
mCopies = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,7 +464,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
mColorMode = other.mColorMode;
|
||||
mFittingMode = other.mFittingMode;
|
||||
mOrientation = other.mOrientation;
|
||||
mCopies = other.mCopies;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -953,6 +1020,44 @@ public final class PrintAttributes implements Parcelable {
|
||||
parcel.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((mId == null) ? 0 : mId.hashCode());
|
||||
result = prime * result + ((mLabel == null) ? 0 : mLabel.hashCode());
|
||||
result = prime * result + mWidthMils;
|
||||
result = prime * result + mHeightMils;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MediaSize other = (MediaSize) obj;
|
||||
if (!TextUtils.equals(mId, other.mId)) {
|
||||
return false;
|
||||
}
|
||||
if (!TextUtils.equals(mLabel, other.mLabel)) {
|
||||
return false;
|
||||
}
|
||||
if (mWidthMils != other.mWidthMils) {
|
||||
return false;
|
||||
}
|
||||
if (mHeightMils != other.mHeightMils) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -1060,6 +1165,44 @@ public final class PrintAttributes implements Parcelable {
|
||||
parcel.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((mId == null) ? 0 : mId.hashCode());
|
||||
result = prime * result + ((mLabel == null) ? 0 : mLabel.hashCode());
|
||||
result = prime * result + mHorizontalDpi;
|
||||
result = prime * result + mVerticalDpi;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Resolution other = (Resolution) obj;
|
||||
if (!TextUtils.equals(mId, other.mId)) {
|
||||
return false;
|
||||
}
|
||||
if (!TextUtils.equals(mLabel, other.mLabel)) {
|
||||
return false;
|
||||
}
|
||||
if (mHorizontalDpi != other.mHorizontalDpi) {
|
||||
return false;
|
||||
}
|
||||
if (mVerticalDpi != other.mVerticalDpi) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -1165,6 +1308,44 @@ public final class PrintAttributes implements Parcelable {
|
||||
parcel.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + mBottomMils;
|
||||
result = prime * result + mLeftMils;
|
||||
result = prime * result + mRightMils;
|
||||
result = prime * result + mTopMils;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Margins other = (Margins) obj;
|
||||
if (mBottomMils != other.mBottomMils) {
|
||||
return false;
|
||||
}
|
||||
if (mLeftMils != other.mLeftMils) {
|
||||
return false;
|
||||
}
|
||||
if (mRightMils != other.mRightMils) {
|
||||
return false;
|
||||
}
|
||||
if (mTopMils != other.mTopMils) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -1234,6 +1415,36 @@ public final class PrintAttributes implements Parcelable {
|
||||
parcel.readCharSequence());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((mId == null) ? 0 : mId.hashCode());
|
||||
result = prime * result + ((mLabel == null) ? 0 : mLabel.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;
|
||||
}
|
||||
Tray other = (Tray) obj;
|
||||
if (!TextUtils.equals(mId, other.mId)) {
|
||||
return false;
|
||||
}
|
||||
if (!TextUtils.equals(mLabel, other.mLabel)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -1246,21 +1457,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("PrintAttributes{");
|
||||
builder.append("mediaSize: ").append(mMediaSize);
|
||||
builder.append(", resolution: ").append(mResolution);
|
||||
builder.append(", margins: ").append(mMargins);
|
||||
builder.append(", duplexMode: ").append(duplexModeToString(mDuplexMode));
|
||||
builder.append(", colorMode: ").append(colorModeToString(mColorMode));
|
||||
builder.append(", fittingMode: ").append(fittingModeToString(mFittingMode));
|
||||
builder.append(", orientation: ").append(orientationToString(mOrientation));
|
||||
builder.append(", copies: ").append(mCopies);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String duplexModeToString(int duplexMode) {
|
||||
switch (duplexMode) {
|
||||
case DUPLEX_MODE_NONE: {
|
||||
@@ -1412,7 +1608,7 @@ public final class PrintAttributes implements Parcelable {
|
||||
* @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
|
||||
*/
|
||||
public Builder setDuplexMode(int duplexMode) {
|
||||
if (Integer.bitCount(duplexMode) != 1) {
|
||||
if (Integer.bitCount(duplexMode) > 1) {
|
||||
throw new IllegalArgumentException("can specify at most one duplexMode bit.");
|
||||
}
|
||||
mAttributes.setDuplexMode(duplexMode);
|
||||
@@ -1470,17 +1666,6 @@ public final class PrintAttributes implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of copies.
|
||||
*
|
||||
* @param copyCount A greater or equal to zero copy count.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setCopyCount(int copyCount) {
|
||||
mAttributes.setCopies(copyCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PrintAttributes} instance.
|
||||
*
|
||||
|
||||
@@ -41,7 +41,7 @@ import java.util.List;
|
||||
* <li>
|
||||
* After every call to {@link #onLayout(PrintAttributes, PrintAttributes,
|
||||
* CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to
|
||||
* {@link #onWrite(List, FileDescriptor, CancellationSignal, WriteResultCallback)}
|
||||
* {@link #onWrite(PageRange[], FileDescriptor, CancellationSignal, WriteResultCallback)}
|
||||
* asking you to write a PDF file with the content for specific pages.
|
||||
* </li>
|
||||
* <li>
|
||||
@@ -64,7 +64,7 @@ import java.util.List;
|
||||
* PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} on
|
||||
* the UI thread (assuming onStart initializes resources needed for layout).
|
||||
* This will ensure that the UI does not change while you are laying out the
|
||||
* printed content. Then you can handle {@link #onWrite(List, FileDescriptor,
|
||||
* printed content. Then you can handle {@link #onWrite(PageRange[], FileDescriptor,
|
||||
* CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another
|
||||
* thread. This will ensure that the UI is frozen for the minimal amount of
|
||||
* time. Also this assumes that you will generate the printed content in
|
||||
@@ -141,7 +141,7 @@ public abstract class PrintDocumentAdapter {
|
||||
* made on the main thread.
|
||||
* </p>
|
||||
*
|
||||
* @param pages The pages whose content to print.
|
||||
* @param pages The pages whose content to print - non-overlapping in ascending order.
|
||||
* @param destination The destination file descriptor to which to write.
|
||||
* @param cancellationSignal Signal for observing cancel writing requests.
|
||||
* @param callback Callback to inform the system for the write result.
|
||||
@@ -149,7 +149,7 @@ public abstract class PrintDocumentAdapter {
|
||||
* @see WriteResultCallback
|
||||
* @see CancellationSignal
|
||||
*/
|
||||
public abstract void onWrite(List<PageRange> pages, FileDescriptor destination,
|
||||
public abstract void onWrite(PageRange[] pages, FileDescriptor destination,
|
||||
CancellationSignal cancellationSignal, WriteResultCallback callback);
|
||||
|
||||
/**
|
||||
@@ -163,7 +163,7 @@ public abstract class PrintDocumentAdapter {
|
||||
|
||||
/**
|
||||
* Base class for implementing a callback for the result of {@link
|
||||
* PrintDocumentAdapter#onWrite(List, FileDescriptor, CancellationSignal,
|
||||
* PrintDocumentAdapter#onWrite(PageRange[], FileDescriptor, CancellationSignal,
|
||||
* WriteResultCallback)}.
|
||||
*/
|
||||
public static abstract class WriteResultCallback {
|
||||
@@ -178,9 +178,9 @@ public abstract class PrintDocumentAdapter {
|
||||
/**
|
||||
* Notifies that all the data was written.
|
||||
*
|
||||
* @param pages The pages that were written.
|
||||
* @param pages The pages that were written. Cannot be null or empty.
|
||||
*/
|
||||
public void onWriteFinished(List<PageRange> pages) {
|
||||
public void onWriteFinished(PageRange[] pages) {
|
||||
/* do nothing - stub */
|
||||
}
|
||||
|
||||
@@ -192,6 +192,13 @@ public abstract class PrintDocumentAdapter {
|
||||
public void onWriteFailed(CharSequence error) {
|
||||
/* do nothing - stub */
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that write was cancelled as a result of a cancellation request.
|
||||
*/
|
||||
public void onWriteCancelled() {
|
||||
/* do nothing - stub */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,7 +218,7 @@ public abstract class PrintDocumentAdapter {
|
||||
/**
|
||||
* Notifies that the layout finished and whether the content changed.
|
||||
*
|
||||
* @param info An info object describing the document.
|
||||
* @param info An info object describing the document. Cannot be null.
|
||||
* @param changed Whether the layout changed.
|
||||
*
|
||||
* @see PrintDocumentInfo
|
||||
@@ -228,5 +235,12 @@ public abstract class PrintDocumentAdapter {
|
||||
public void onLayoutFailed(CharSequence error) {
|
||||
/* do nothing - stub */
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that layout was cancelled as a result of a cancellation request.
|
||||
*/
|
||||
public void onLayoutCancelled() {
|
||||
/* do nothing - stub */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,36 @@ public final class PrintDocumentInfo implements Parcelable {
|
||||
parcel.writeInt(mContentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + mContentType;
|
||||
result = prime * result + mPageCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PrintDocumentInfo other = (PrintDocumentInfo) obj;
|
||||
if (mContentType != other.mContentType) {
|
||||
return false;
|
||||
}
|
||||
if (mPageCount != other.mPageCount) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -55,6 +55,9 @@ public final class PrintJob {
|
||||
* @return The print job info.
|
||||
*/
|
||||
public PrintJobInfo getInfo() {
|
||||
if (isInImmutableState()) {
|
||||
return mCachedInfo;
|
||||
}
|
||||
PrintJobInfo info = mPrintManager.getPrintJobInfo(mId);
|
||||
if (info != null) {
|
||||
mCachedInfo = info;
|
||||
@@ -66,7 +69,15 @@ public final class PrintJob {
|
||||
* Cancels this print job.
|
||||
*/
|
||||
public void cancel() {
|
||||
mPrintManager.cancelPrintJob(mId);
|
||||
if (!isInImmutableState()) {
|
||||
mPrintManager.cancelPrintJob(mId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInImmutableState() {
|
||||
final int state = mCachedInfo.getState();
|
||||
return state == PrintJobInfo.STATE_COMPLETED
|
||||
|| state == PrintJobInfo.STATE_CANCELED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,8 @@ package android.print;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class represents the description of a print job.
|
||||
*/
|
||||
@@ -119,6 +121,9 @@ public final class PrintJobInfo implements Parcelable {
|
||||
/** Optional tag assigned by a print service.*/
|
||||
private String mTag;
|
||||
|
||||
/** How many copies to print. */
|
||||
private int mCopies;
|
||||
|
||||
/** The pages to print */
|
||||
private PageRange[] mPageRanges;
|
||||
|
||||
@@ -142,6 +147,8 @@ public final class PrintJobInfo implements Parcelable {
|
||||
mAppId = other.mAppId;
|
||||
mUserId = other.mUserId;
|
||||
mTag = other.mTag;
|
||||
mCopies = other.mCopies;
|
||||
mPageRanges = other.mPageRanges;
|
||||
mAttributes = other.mAttributes;
|
||||
mDocumentInfo = other.mDocumentInfo;
|
||||
}
|
||||
@@ -154,8 +161,13 @@ public final class PrintJobInfo implements Parcelable {
|
||||
mAppId = parcel.readInt();
|
||||
mUserId = parcel.readInt();
|
||||
mTag = parcel.readString();
|
||||
mCopies = parcel.readInt();
|
||||
if (parcel.readInt() == 1) {
|
||||
mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
|
||||
Parcelable[] parcelables = parcel.readParcelableArray(null);
|
||||
mPageRanges = new PageRange[parcelables.length];
|
||||
for (int i = 0; i < parcelables.length; i++) {
|
||||
mPageRanges[i] = (PageRange) parcelables[i];
|
||||
}
|
||||
}
|
||||
if (parcel.readInt() == 1) {
|
||||
mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
|
||||
@@ -309,6 +321,29 @@ public final class PrintJobInfo implements Parcelable {
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of copies.
|
||||
*
|
||||
* @return The number of copies or zero if not set.
|
||||
*/
|
||||
public int getCopies() {
|
||||
return mCopies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of copies.
|
||||
*
|
||||
* @param copyCount The number of copies.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setCopies(int copyCount) {
|
||||
if (copyCount < 1) {
|
||||
throw new IllegalArgumentException("Copies must be more than one.");
|
||||
}
|
||||
mCopies = copyCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the included pages.
|
||||
*
|
||||
@@ -385,6 +420,7 @@ public final class PrintJobInfo implements Parcelable {
|
||||
parcel.writeInt(mAppId);
|
||||
parcel.writeInt(mUserId);
|
||||
parcel.writeString(mTag);
|
||||
parcel.writeInt(mCopies);
|
||||
if (mPageRanges != null) {
|
||||
parcel.writeInt(1);
|
||||
parcel.writeParcelableArray(mPageRanges, flags);
|
||||
@@ -413,10 +449,14 @@ public final class PrintJobInfo implements Parcelable {
|
||||
builder.append(", id: ").append(mId);
|
||||
builder.append(", status: ").append(stateToString(mState));
|
||||
builder.append(", printer: " + mPrinterId);
|
||||
builder.append(", tag: ").append(mTag);
|
||||
builder.append(", copies: ").append(mCopies);
|
||||
builder.append(", attributes: " + (mAttributes != null
|
||||
? mAttributes.toString() : null));
|
||||
builder.append(", documentInfo: " + (mDocumentInfo != null
|
||||
? mDocumentInfo.toString() : null));
|
||||
builder.append(", pages: " + (mPageRanges != null
|
||||
? Arrays.toString(mPageRanges) : null));
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.IntentSender.SendIntentException;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.ICancellationSignal;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -223,6 +222,11 @@ public final class PrintManager {
|
||||
}
|
||||
|
||||
private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private CancellationSignal mLayoutOrWriteCancellation;
|
||||
|
||||
private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
|
||||
|
||||
private Handler mHandler; // Strong reference OK - cleared in finish()
|
||||
@@ -239,22 +243,36 @@ public final class PrintManager {
|
||||
|
||||
@Override
|
||||
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
|
||||
ILayoutResultCallback callback, Bundle metadata) {
|
||||
ILayoutResultCallback callback, Bundle metadata, int sequence) {
|
||||
synchronized (mLock) {
|
||||
if (mLayoutOrWriteCancellation != null) {
|
||||
mLayoutOrWriteCancellation.cancel();
|
||||
}
|
||||
}
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = oldAttributes;
|
||||
args.arg2 = newAttributes;
|
||||
args.arg3 = callback;
|
||||
args.arg4 = metadata;
|
||||
args.argi1 = sequence;
|
||||
mHandler.removeMessages(MyHandler.MSG_LAYOUT);
|
||||
mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(List<PageRange> pages, ParcelFileDescriptor fd,
|
||||
IWriteResultCallback callback) {
|
||||
public void write(PageRange[] pages, ParcelFileDescriptor fd,
|
||||
IWriteResultCallback callback, int sequence) {
|
||||
synchronized (mLock) {
|
||||
if (mLayoutOrWriteCancellation != null) {
|
||||
mLayoutOrWriteCancellation.cancel();
|
||||
}
|
||||
}
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = pages;
|
||||
args.arg2 = fd.getFileDescriptor();
|
||||
args.arg3 = callback;
|
||||
args.argi1 = sequence;
|
||||
mHandler.removeMessages(MyHandler.MSG_WRITE);
|
||||
mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
|
||||
}
|
||||
|
||||
@@ -283,7 +301,6 @@ public final class PrintManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleMessage(Message message) {
|
||||
if (isFinished()) {
|
||||
return;
|
||||
@@ -295,42 +312,116 @@ public final class PrintManager {
|
||||
|
||||
case MSG_LAYOUT: {
|
||||
SomeArgs args = (SomeArgs) message.obj;
|
||||
PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
|
||||
PrintAttributes newAttributes = (PrintAttributes) args.arg2;
|
||||
ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
|
||||
Bundle metadata = (Bundle) args.arg4;
|
||||
final PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
|
||||
final PrintAttributes newAttributes = (PrintAttributes) args.arg2;
|
||||
final ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
|
||||
final Bundle metadata = (Bundle) args.arg4;
|
||||
final int sequence = args.argi1;
|
||||
args.recycle();
|
||||
|
||||
try {
|
||||
ICancellationSignal remoteSignal = CancellationSignal.createTransport();
|
||||
callback.onLayoutStarted(remoteSignal);
|
||||
|
||||
mDocumentAdapter.onLayout(oldAttributes, newAttributes,
|
||||
CancellationSignal.fromTransport(remoteSignal),
|
||||
new LayoutResultCallbackWrapper(callback), metadata);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error printing", re);
|
||||
CancellationSignal cancellation = new CancellationSignal();
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = cancellation;
|
||||
}
|
||||
|
||||
mDocumentAdapter.onLayout(oldAttributes, newAttributes,
|
||||
cancellation, new LayoutResultCallback() {
|
||||
@Override
|
||||
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
|
||||
if (info == null) {
|
||||
throw new IllegalArgumentException("info cannot be null");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = null;
|
||||
}
|
||||
try {
|
||||
callback.onLayoutFinished(info, changed, sequence);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutFailed(CharSequence error) {
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = null;
|
||||
}
|
||||
try {
|
||||
callback.onLayoutFailed(error, sequence);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutCancelled() {
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = null;
|
||||
}
|
||||
}
|
||||
}, metadata);
|
||||
} break;
|
||||
|
||||
case MSG_WRITE: {
|
||||
SomeArgs args = (SomeArgs) message.obj;
|
||||
List<PageRange> pages = (List<PageRange>) args.arg1;
|
||||
FileDescriptor fd = (FileDescriptor) args.arg2;
|
||||
IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
|
||||
final PageRange[] pages = (PageRange[]) args.arg1;
|
||||
final FileDescriptor fd = (FileDescriptor) args.arg2;
|
||||
final IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
|
||||
final int sequence = args.argi1;
|
||||
args.recycle();
|
||||
|
||||
try {
|
||||
ICancellationSignal remoteSignal = CancellationSignal.createTransport();
|
||||
callback.onWriteStarted(remoteSignal);
|
||||
|
||||
mDocumentAdapter.onWrite(pages, fd,
|
||||
CancellationSignal.fromTransport(remoteSignal),
|
||||
new WriteResultCallbackWrapper(callback, fd));
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error printing", re);
|
||||
IoUtils.closeQuietly(fd);
|
||||
CancellationSignal cancellation = new CancellationSignal();
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = cancellation;
|
||||
}
|
||||
|
||||
mDocumentAdapter.onWrite(pages, fd, cancellation,
|
||||
new WriteResultCallback() {
|
||||
@Override
|
||||
public void onWriteFinished(PageRange[] pages) {
|
||||
if (pages == null) {
|
||||
throw new IllegalArgumentException("pages cannot be null");
|
||||
}
|
||||
if (pages.length == 0) {
|
||||
throw new IllegalArgumentException("pages cannot be empty");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = null;
|
||||
}
|
||||
// Close before notifying the other end. We want
|
||||
// to be ready by the time we announce it.
|
||||
IoUtils.closeQuietly(fd);
|
||||
try {
|
||||
callback.onWriteFinished(pages, sequence);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onWriteFinished", re);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteFailed(CharSequence error) {
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = null;
|
||||
}
|
||||
// Close before notifying the other end. We want
|
||||
// to be ready by the time we announce it.
|
||||
IoUtils.closeQuietly(fd);
|
||||
try {
|
||||
callback.onWriteFailed(error, sequence);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onWriteFailed", re);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteCancelled() {
|
||||
synchronized (mLock) {
|
||||
mLayoutOrWriteCancellation = null;
|
||||
}
|
||||
// Just close the fd for now.
|
||||
IoUtils.closeQuietly(fd);
|
||||
}
|
||||
});
|
||||
} break;
|
||||
|
||||
case MSG_FINISH: {
|
||||
@@ -346,67 +437,4 @@ public final class PrintManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class WriteResultCallbackWrapper extends WriteResultCallback {
|
||||
|
||||
private final IWriteResultCallback mWrappedCallback;
|
||||
private final FileDescriptor mFd;
|
||||
|
||||
public WriteResultCallbackWrapper(IWriteResultCallback callback,
|
||||
FileDescriptor fd) {
|
||||
mWrappedCallback = callback;
|
||||
mFd = fd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteFinished(List<PageRange> pages) {
|
||||
try {
|
||||
// Close before notifying the other end. We want
|
||||
// to be ready by the time we announce it.
|
||||
IoUtils.closeQuietly(mFd);
|
||||
mWrappedCallback.onWriteFinished(pages);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onWriteFinished", re);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteFailed(CharSequence error) {
|
||||
try {
|
||||
// Close before notifying the other end. We want
|
||||
// to be ready by the time we announce it.
|
||||
IoUtils.closeQuietly(mFd);
|
||||
mWrappedCallback.onWriteFailed(error);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onWriteFailed", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LayoutResultCallbackWrapper extends LayoutResultCallback {
|
||||
|
||||
private final ILayoutResultCallback mWrappedCallback;
|
||||
|
||||
public LayoutResultCallbackWrapper(ILayoutResultCallback callback) {
|
||||
mWrappedCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
|
||||
try {
|
||||
mWrappedCallback.onLayoutFinished(info, changed);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutFailed(CharSequence error) {
|
||||
try {
|
||||
mWrappedCallback.onLayoutFailed(error);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,6 @@ public final class PrinterInfo implements Parcelable {
|
||||
*/
|
||||
public static final int DEFAULT_UNDEFINED = -1;
|
||||
|
||||
private static final int MIN_COPIES = 1;
|
||||
|
||||
private static final int PROPERTY_MEDIA_SIZE = 0;
|
||||
private static final int PROPERTY_RESOLUTION = 1;
|
||||
private static final int PROPERTY_INPUT_TRAY = 2;
|
||||
@@ -240,9 +238,6 @@ public final class PrinterInfo implements Parcelable {
|
||||
public void getDefaults(PrintAttributes outAttributes) {
|
||||
outAttributes.clear();
|
||||
|
||||
// TODO: Do we want a printer to specify default copies?
|
||||
outAttributes.setCopies(MIN_COPIES);
|
||||
|
||||
outAttributes.setMargins(mDefaultMargins);
|
||||
|
||||
final int mediaSizeIndex = mDefaults.get(PROPERTY_MEDIA_SIZE);
|
||||
|
||||
@@ -61,6 +61,9 @@ public final class PrintJob {
|
||||
* @return The print job info.
|
||||
*/
|
||||
public PrintJobInfo getInfo() {
|
||||
if (isInImmutableState()) {
|
||||
return mCachedInfo;
|
||||
}
|
||||
PrintJobInfo info = null;
|
||||
try {
|
||||
info = mPrintServiceClient.getPrintJobInfo(mCachedInfo.getId());
|
||||
@@ -182,6 +185,9 @@ public final class PrintJob {
|
||||
* @return True if the tag was set, false otherwise.
|
||||
*/
|
||||
public boolean setTag(String tag) {
|
||||
if (isInImmutableState()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return mPrintServiceClient.setPrintJobTag(mCachedInfo.getId(), tag);
|
||||
} catch (RemoteException re) {
|
||||
@@ -210,6 +216,12 @@ public final class PrintJob {
|
||||
return mCachedInfo.getId();
|
||||
}
|
||||
|
||||
private boolean isInImmutableState() {
|
||||
final int state = mCachedInfo.getState();
|
||||
return state == PrintJobInfo.STATE_COMPLETED
|
||||
|| state == PrintJobInfo.STATE_CANCELED;
|
||||
}
|
||||
|
||||
private boolean setState(int state) {
|
||||
try {
|
||||
if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state)) {
|
||||
|
||||
@@ -34,12 +34,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:layout_marginLeft="32dip"
|
||||
android:layout_marginTop="32dip"
|
||||
android:layout_marginRight="32dip"
|
||||
android:layout_marginBottom="12dip"
|
||||
android:layout_row="0"
|
||||
android:layout_column="0"
|
||||
android:layout_columnSpan="2"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall">
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Spinner>
|
||||
|
||||
<!-- Copies -->
|
||||
@@ -57,7 +58,8 @@
|
||||
android:layout_gravity="bottom"
|
||||
android:inputType="numberDecimal"
|
||||
android:selectAllOnFocus="true"
|
||||
android:minWidth="150dip">
|
||||
android:minWidth="150dip"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</view>
|
||||
|
||||
<TextView
|
||||
@@ -86,7 +88,8 @@
|
||||
android:layout_marginBottom="12dip"
|
||||
android:layout_row="2"
|
||||
android:layout_column="1"
|
||||
android:minWidth="150dip">
|
||||
android:minWidth="150dip"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Spinner>
|
||||
|
||||
<TextView
|
||||
@@ -114,7 +117,8 @@
|
||||
android:layout_marginBottom="12dip"
|
||||
android:layout_row="4"
|
||||
android:layout_column="0"
|
||||
android:minWidth="150dip">
|
||||
android:minWidth="150dip"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Spinner>
|
||||
|
||||
<TextView
|
||||
@@ -142,7 +146,8 @@
|
||||
android:layout_marginBottom="12dip"
|
||||
android:layout_row="4"
|
||||
android:layout_column="1"
|
||||
android:minWidth="150dip">
|
||||
android:minWidth="150dip"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Spinner>
|
||||
|
||||
<TextView
|
||||
@@ -169,7 +174,8 @@
|
||||
android:layout_marginRight="12dip"
|
||||
android:layout_row="6"
|
||||
android:layout_column="0"
|
||||
android:minWidth="150dip">
|
||||
android:minWidth="150dip"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Spinner>
|
||||
|
||||
<view
|
||||
@@ -186,10 +192,12 @@
|
||||
android:minWidth="150dip"
|
||||
android:hint="@string/pages_range_example"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:visibility="gone">
|
||||
android:visibility="gone"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</view>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/page_range_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="32dip"
|
||||
@@ -231,7 +239,8 @@
|
||||
android:layout_columnSpan="2"
|
||||
android:text="@string/print_preview"
|
||||
android:gravity="left|center_vertical"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Button>
|
||||
|
||||
<ImageView
|
||||
@@ -269,7 +278,8 @@
|
||||
android:layout_columnSpan="2"
|
||||
android:padding="0dip"
|
||||
android:text="@string/print_button"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
||||
</Button>
|
||||
|
||||
</GridLayout>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<!-- Title of the PrintSpooler application. [CHAR LIMIT=50] -->
|
||||
<string name="app_label">Print Spooler</string>
|
||||
@@ -38,7 +38,7 @@
|
||||
<string name="label_orientation">ORIENTATION</string>
|
||||
|
||||
<!-- Label of the page selection widget. [CHAR LIMIT=20] -->
|
||||
<string name="label_pages">PAGES</string>
|
||||
<string name="label_pages">PAGES (<xliff:g id="page_count" example="5">%1$s</xliff:g>)</string>
|
||||
|
||||
<!-- Page range exmple used as a hint of how to specify such. [CHAR LIMIT=15] -->
|
||||
<string name="pages_range_example">e.g. 1–5, 8</string>
|
||||
@@ -52,6 +52,9 @@
|
||||
<!-- Title of the message that the printing application crashed. [CHAR LIMIT=50] -->
|
||||
<string name="printing_app_crashed">Printing app crashed</string>
|
||||
|
||||
<!-- Title if the number of pages in a printed document is unknown. [CHAR LIMIT=20] -->
|
||||
<string name="page_count_unknown">unknown</string>
|
||||
|
||||
<!-- Color mode labels. -->
|
||||
<string-array name="color_mode_labels">
|
||||
<!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,9 @@ package com.android.printspooler;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.print.IPrintClient;
|
||||
@@ -39,6 +42,7 @@ import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
@@ -59,9 +63,9 @@ import java.util.Map;
|
||||
|
||||
public class PrintSpooler {
|
||||
|
||||
private static final String LOG_TAG = PrintSpooler.class.getSimpleName();
|
||||
private static final String LOG_TAG = "PrintSpooler";
|
||||
|
||||
private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
|
||||
private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = true;
|
||||
|
||||
private static final boolean DEBUG_PERSISTENCE = true;
|
||||
|
||||
@@ -81,6 +85,8 @@ public class PrintSpooler {
|
||||
|
||||
private final PersistenceManager mPersistanceManager;
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public IPrintSpoolerClient mClient;
|
||||
@@ -97,6 +103,7 @@ public class PrintSpooler {
|
||||
private PrintSpooler(Context context) {
|
||||
mContext = context;
|
||||
mPersistanceManager = new PersistenceManager(context);
|
||||
mHandler = new MyHandler(context.getMainLooper());
|
||||
}
|
||||
|
||||
public void setCleint(IPrintSpoolerClient client) {
|
||||
@@ -112,36 +119,25 @@ public class PrintSpooler {
|
||||
}
|
||||
|
||||
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
|
||||
IPrintSpoolerClient client = null;
|
||||
synchronized (mLock) {
|
||||
client = mClient;
|
||||
}
|
||||
if (client != null) {
|
||||
try {
|
||||
client.onStartPrinterDiscovery(observer);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
|
||||
}
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = mClient;
|
||||
args.arg2 = observer;
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
|
||||
args).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPrinterDiscovery() {
|
||||
IPrintSpoolerClient client = null;
|
||||
synchronized (mLock) {
|
||||
client = mClient;
|
||||
}
|
||||
if (client != null) {
|
||||
try {
|
||||
client.onStopPrinterDiscovery();
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
|
||||
}
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY,
|
||||
mClient).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state, int appId) {
|
||||
List<PrintJobInfo> foundPrintJobs = null;
|
||||
synchronized (mLock) {
|
||||
List<PrintJobInfo> foundPrintJobs = null;
|
||||
final int printJobCount = mPrintJobs.size();
|
||||
for (int i = 0; i < printJobCount; i++) {
|
||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
||||
@@ -162,8 +158,8 @@ public class PrintSpooler {
|
||||
foundPrintJobs.add(printJob);
|
||||
}
|
||||
}
|
||||
return foundPrintJobs;
|
||||
}
|
||||
return foundPrintJobs;
|
||||
}
|
||||
|
||||
public PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
|
||||
@@ -172,11 +168,12 @@ public class PrintSpooler {
|
||||
for (int i = 0; i < printJobCount; i++) {
|
||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
||||
if (printJob.getId() == printJobId
|
||||
&& (appId == PrintManager.APP_ID_ANY || appId == printJob.getAppId())) {
|
||||
&& (appId == PrintManager.APP_ID_ANY
|
||||
|| appId == printJob.getAppId())) {
|
||||
return printJob;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +214,7 @@ public class PrintSpooler {
|
||||
Map<ComponentName, List<PrintJobInfo>> activeJobsPerServiceMap =
|
||||
new HashMap<ComponentName, List<PrintJobInfo>>();
|
||||
|
||||
synchronized(mLock) {
|
||||
synchronized (mLock) {
|
||||
if (mClient == null) {
|
||||
throw new IllegalStateException("Client cannot be null.");
|
||||
}
|
||||
@@ -265,16 +262,25 @@ public class PrintSpooler {
|
||||
for (int i = 0; i < printJobCount; i++) {
|
||||
PrintJobInfo printJob = printJobs.get(i);
|
||||
if (printJob.getState() == PrintJobInfo.STATE_QUEUED) {
|
||||
callOnPrintJobQueuedQuietly(client, printJob);
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = client;
|
||||
args.arg2 = new PrintJobInfo(printJob);
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
|
||||
args).sendToTarget();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callOnAllPrintJobsForServiceHandledQuietly(client, service);
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = client;
|
||||
args.arg2 = service;
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
|
||||
args).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
if (allPrintJobsHandled) {
|
||||
callOnAllPrintJobsHandledQuietly(client);
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED,
|
||||
client).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,37 +303,43 @@ public class PrintSpooler {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public boolean writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
|
||||
public void writePrintJobData(final ParcelFileDescriptor fd, final int printJobId) {
|
||||
final PrintJobInfo printJob;
|
||||
synchronized (mLock) {
|
||||
FileInputStream in = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
File file = generateFileForPrintJob(printJobId);
|
||||
in = new FileInputStream(file);
|
||||
out = new FileOutputStream(fd.getFileDescriptor());
|
||||
printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
}
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
FileInputStream in = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
if (printJob != null) {
|
||||
File file = generateFileForPrintJob(printJobId);
|
||||
in = new FileInputStream(file);
|
||||
out = new FileOutputStream(fd.getFileDescriptor());
|
||||
}
|
||||
final byte[] buffer = new byte[8192];
|
||||
while (true) {
|
||||
final int readByteCount = in.read(buffer);
|
||||
if (readByteCount < 0) {
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
out.write(buffer, 0, readByteCount);
|
||||
}
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
Log.e(LOG_TAG, "Error writing print job data!", fnfe);
|
||||
} catch (IOException ioe) {
|
||||
Log.e(LOG_TAG, "Error writing print job data!", ioe);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
IoUtils.closeQuietly(out);
|
||||
IoUtils.closeQuietly(fd);
|
||||
}
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
Log.e(LOG_TAG, "Error writing print job data!", fnfe);
|
||||
} catch (IOException ioe) {
|
||||
Log.e(LOG_TAG, "Error writing print job data!", ioe);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
IoUtils.closeQuietly(out);
|
||||
IoUtils.closeQuietly(fd);
|
||||
Log.i(LOG_TAG, "[END WRITE]");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||
}
|
||||
|
||||
public File generateFileForPrintJob(int printJobId) {
|
||||
@@ -354,28 +366,24 @@ public class PrintSpooler {
|
||||
public boolean setPrintJobState(int printJobId, int state) {
|
||||
boolean success = false;
|
||||
|
||||
boolean allPrintJobsHandled = false;
|
||||
boolean allPrintJobsForServiceHandled = false;
|
||||
|
||||
IPrintSpoolerClient client = null;
|
||||
PrintJobInfo queuedPrintJob = null;
|
||||
PrintJobInfo removedPrintJob = null;
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mClient == null) {
|
||||
throw new IllegalStateException("Client cannot be null.");
|
||||
}
|
||||
client = mClient;
|
||||
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null && printJob.getState() < state) {
|
||||
success = true;
|
||||
printJob.setState(state);
|
||||
|
||||
if (DEBUG_PRINT_JOB_LIFECYCLE) {
|
||||
Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
|
||||
}
|
||||
|
||||
// TODO: Update notifications.
|
||||
switch (state) {
|
||||
case PrintJobInfo.STATE_COMPLETED:
|
||||
case PrintJobInfo.STATE_CANCELED: {
|
||||
removedPrintJob = printJob;
|
||||
removePrintJobLocked(printJob);
|
||||
|
||||
// No printer means creation of a print job was cancelled,
|
||||
@@ -387,83 +395,46 @@ public class PrintSpooler {
|
||||
return true;
|
||||
}
|
||||
|
||||
allPrintJobsHandled = !hasActivePrintJobsLocked();
|
||||
allPrintJobsForServiceHandled = !hasActivePrintJobsForServiceLocked(
|
||||
printerId.getService());
|
||||
ComponentName service = printerId.getService();
|
||||
if (!hasActivePrintJobsForServiceLocked(service)) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = mClient;
|
||||
args.arg2 = service;
|
||||
mHandler.obtainMessage(
|
||||
MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
|
||||
args).sendToTarget();
|
||||
}
|
||||
|
||||
if (!hasActivePrintJobsLocked()) {
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED,
|
||||
mClient).sendToTarget();
|
||||
}
|
||||
} break;
|
||||
|
||||
case PrintJobInfo.STATE_QUEUED: {
|
||||
queuedPrintJob = new PrintJobInfo(printJob);
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = mClient;
|
||||
args.arg2 = new PrintJobInfo(printJob);
|
||||
mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
|
||||
args).sendToTarget();
|
||||
} break;
|
||||
}
|
||||
if (DEBUG_PRINT_JOB_LIFECYCLE) {
|
||||
Slog.i(LOG_TAG, "[STATUS CHANGED] " + printJob);
|
||||
|
||||
if (shouldPersistPrintJob(printJob)) {
|
||||
mPersistanceManager.writeStateLocked();
|
||||
}
|
||||
mPersistanceManager.writeStateLocked();
|
||||
}
|
||||
}
|
||||
|
||||
if (queuedPrintJob != null) {
|
||||
callOnPrintJobQueuedQuietly(client, queuedPrintJob);
|
||||
}
|
||||
|
||||
if (allPrintJobsForServiceHandled) {
|
||||
callOnAllPrintJobsForServiceHandledQuietly(client,
|
||||
removedPrintJob.getPrinterId().getService());
|
||||
}
|
||||
|
||||
if (allPrintJobsHandled) {
|
||||
callOnAllPrintJobsHandledQuietly(client);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void callOnPrintJobQueuedQuietly(IPrintSpoolerClient client,
|
||||
PrintJobInfo printJob) {
|
||||
try {
|
||||
client.onPrintJobQueued(printJob);
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
|
||||
}
|
||||
}
|
||||
|
||||
private void callOnAllPrintJobsForServiceHandledQuietly(IPrintSpoolerClient client,
|
||||
ComponentName service) {
|
||||
try {
|
||||
client.onAllPrintJobsForServiceHandled(service);
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error notify for all print jobs per service handled.", re);
|
||||
}
|
||||
}
|
||||
|
||||
private void callOnAllPrintJobsHandledQuietly(final IPrintSpoolerClient client) {
|
||||
// This has to run on the tread that is persisting the current state
|
||||
// since this call may result in the system unbinding from the spooler
|
||||
// and as a result the spooler process may get killed before the write
|
||||
// completes.
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
client.onAllPrintJobsHandled();
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||
}
|
||||
|
||||
private boolean hasActivePrintJobsLocked() {
|
||||
final int printJobCount = mPrintJobs.size();
|
||||
for (int i = 0; i < printJobCount; i++) {
|
||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
||||
switch (printJob.getState()) {
|
||||
case PrintJobInfo.STATE_QUEUED:
|
||||
case PrintJobInfo.STATE_STARTED: {
|
||||
return true;
|
||||
}
|
||||
if (!isActiveState(printJob.getState())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -473,72 +444,89 @@ public class PrintSpooler {
|
||||
final int printJobCount = mPrintJobs.size();
|
||||
for (int i = 0; i < printJobCount; i++) {
|
||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
||||
switch (printJob.getState()) {
|
||||
case PrintJobInfo.STATE_QUEUED:
|
||||
case PrintJobInfo.STATE_STARTED: {
|
||||
if (printJob.getPrinterId().getService().equals(service)) {
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
if (!isActiveState(printJob.getState())
|
||||
&& printJob.getPrinterId().getService().equals(service)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isActiveState(int printJobState) {
|
||||
return printJobState != PrintJobInfo.STATE_CREATED
|
||||
|| printJobState != PrintJobInfo.STATE_QUEUED
|
||||
|| printJobState != PrintJobInfo.STATE_STARTED;
|
||||
}
|
||||
|
||||
public boolean setPrintJobTag(int printJobId, String tag) {
|
||||
synchronized (mLock) {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
String printJobTag = printJob.getTag();
|
||||
if (printJobTag == null) {
|
||||
if (tag == null) {
|
||||
return false;
|
||||
}
|
||||
} else if (printJobTag.equals(tag)) {
|
||||
return false;
|
||||
}
|
||||
printJob.setTag(tag);
|
||||
mPersistanceManager.writeStateLocked();
|
||||
if (shouldPersistPrintJob(printJob)) {
|
||||
mPersistanceManager.writeStateLocked();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
|
||||
public void setPrintJobCopiesNoPersistence(int printJobId, int copies) {
|
||||
synchronized (mLock) {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
printJob.setCopies(copies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPrintJobPrintDocumentInfoNoPersistence(int printJobId, PrintDocumentInfo info) {
|
||||
synchronized (mLock) {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
printJob.setDocumentInfo(info);
|
||||
mPersistanceManager.writeStateLocked();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
|
||||
public void setPrintJobAttributesNoPersistence(int printJobId, PrintAttributes attributes) {
|
||||
synchronized (mLock) {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
printJob.setAttributes(attributes);
|
||||
mPersistanceManager.writeStateLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPrintJobPrinterId(int printJobId, PrinterId printerId) {
|
||||
public void setPrintJobPrinterIdNoPersistence(int printJobId, PrinterId printerId) {
|
||||
synchronized (mLock) {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
printJob.setPrinterId(printerId);
|
||||
mPersistanceManager.writeStateLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setPrintJobPages(int printJobId, PageRange[] pages) {
|
||||
public void setPrintJobPagesNoPersistence(int printJobId, PageRange[] pages) {
|
||||
synchronized (mLock) {
|
||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
||||
if (printJob != null) {
|
||||
printJob.setPages(pages);
|
||||
mPersistanceManager.writeStateLocked();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
|
||||
return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
|
||||
}
|
||||
|
||||
private final class PersistenceManager {
|
||||
@@ -558,6 +546,7 @@ public class PrintSpooler {
|
||||
private static final String ATTR_APP_ID = "appId";
|
||||
private static final String ATTR_USER_ID = "userId";
|
||||
private static final String ATTR_TAG = "tag";
|
||||
private static final String ATTR_COPIES = "copies";
|
||||
|
||||
private static final String TAG_MEDIA_SIZE = "mediaSize";
|
||||
private static final String TAG_RESOLUTION = "resolution";
|
||||
@@ -620,6 +609,9 @@ public class PrintSpooler {
|
||||
}
|
||||
|
||||
private void doWriteStateLocked() {
|
||||
if (DEBUG_PERSISTENCE) {
|
||||
Log.i(LOG_TAG, "[PERSIST START]");
|
||||
}
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = mStatePersistFile.startWrite();
|
||||
@@ -652,6 +644,7 @@ public class PrintSpooler {
|
||||
if (tag != null) {
|
||||
serializer.attribute(null, ATTR_TAG, tag);
|
||||
}
|
||||
serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
|
||||
|
||||
PrinterId printerId = printJob.getPrinterId();
|
||||
if (printerId != null) {
|
||||
@@ -775,6 +768,9 @@ public class PrintSpooler {
|
||||
serializer.endTag(null, TAG_SPOOLER);
|
||||
serializer.endDocument();
|
||||
mStatePersistFile.finishWrite(out);
|
||||
if (DEBUG_PERSISTENCE) {
|
||||
Log.i(LOG_TAG, "[PERSIST END]");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
|
||||
mStatePersistFile.failWrite(out);
|
||||
@@ -861,6 +857,8 @@ public class PrintSpooler {
|
||||
printJob.setUserId(userId);
|
||||
String tag = parser.getAttributeValue(null, ATTR_TAG);
|
||||
printJob.setTag(tag);
|
||||
String copies = parser.getAttributeValue(null, ATTR_TAG);
|
||||
printJob.setCopies(Integer.parseInt(copies));
|
||||
|
||||
parser.next();
|
||||
|
||||
@@ -892,7 +890,9 @@ public class PrintSpooler {
|
||||
parser.next();
|
||||
}
|
||||
if (pageRanges != null) {
|
||||
printJob.setPages((PageRange[]) pageRanges.toArray());
|
||||
PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
|
||||
pageRanges.toArray(pageRangesArray);
|
||||
printJob.setPages(pageRangesArray);
|
||||
}
|
||||
|
||||
skipEmptyTextTags(parser);
|
||||
@@ -1054,4 +1054,93 @@ public class PrintSpooler {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
|
||||
public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4;
|
||||
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 5;
|
||||
|
||||
public MyHandler(Looper looper) {
|
||||
super(looper, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case MSG_ON_START_PRINTER_DISCOVERY: {
|
||||
SomeArgs args = (SomeArgs) message.obj;
|
||||
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
|
||||
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg2;
|
||||
args.recycle();
|
||||
if (client != null) {
|
||||
try {
|
||||
client.onStartPrinterDiscovery(observer);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case MSG_ON_STOP_PRINTER_DISCOVERY: {
|
||||
IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj;
|
||||
if (client != null) {
|
||||
try {
|
||||
client.onStopPrinterDiscovery();
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case MSG_ON_PRINT_JOB_QUEUED: {
|
||||
SomeArgs args = (SomeArgs) message.obj;
|
||||
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
|
||||
PrintJobInfo printJob = (PrintJobInfo) args.arg2;
|
||||
args.recycle();
|
||||
if (client != null) {
|
||||
try {
|
||||
client.onPrintJobQueued(printJob);
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
|
||||
SomeArgs args = (SomeArgs) message.obj;
|
||||
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
|
||||
ComponentName service = (ComponentName) args.arg2;
|
||||
args.recycle();
|
||||
if (client != null) {
|
||||
try {
|
||||
client.onAllPrintJobsForServiceHandled(service);
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error notify for all print jobs per service handled.", re);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
|
||||
final IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj;
|
||||
// This has to run on the tread that is persisting the current state
|
||||
// since this call may result in the system unbinding from the spooler
|
||||
// and as a result the spooler process may get killed before the write
|
||||
// completes.
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
client.onAllPrintJobsHandled();
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,12 +114,11 @@ public final class PrintSpoolerService extends Service {
|
||||
attributes, appId);
|
||||
if (printJob != null) {
|
||||
Intent intent = mStartPrintJobConfigActivityIntent;
|
||||
intent.putExtra(PrintJobConfigActivity.EXTRA_PRINTABLE,
|
||||
intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_DOCUMENT_ADAPTER,
|
||||
printAdapter.asBinder());
|
||||
intent.putExtra(PrintJobConfigActivity.EXTRA_APP_ID, appId);
|
||||
intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_JOB_ID,
|
||||
printJob.getId());
|
||||
intent.putExtra(PrintJobConfigActivity.EXTRA_ATTRIBUTES, attributes);
|
||||
intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_ATTRIBUTES, attributes);
|
||||
|
||||
IntentSender sender = PendingIntent.getActivity(
|
||||
PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package com.android.printspooler;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ICancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.print.ILayoutResultCallback;
|
||||
@@ -26,11 +26,7 @@ import android.print.IPrintDocumentAdapter;
|
||||
import android.print.IWriteResultCallback;
|
||||
import android.print.PageRange;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter.LayoutResultCallback;
|
||||
import android.print.PrintDocumentAdapter.WriteResultCallback;
|
||||
import android.print.PrintDocumentInfo;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
@@ -40,8 +36,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class represents a remote print document adapter instance.
|
||||
@@ -49,461 +43,99 @@ import java.util.List;
|
||||
final class RemotePrintDocumentAdapter {
|
||||
private static final String LOG_TAG = "RemotePrintDocumentAdapter";
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
public static final int STATE_INITIALIZED = 0;
|
||||
public static final int STATE_START_COMPLETED = 1;
|
||||
public static final int STATE_LAYOUT_STARTED = 2;
|
||||
public static final int STATE_LAYOUT_COMPLETED = 3;
|
||||
public static final int STATE_WRITE_STARTED = 4;
|
||||
public static final int STATE_WRITE_COMPLETED = 5;
|
||||
public static final int STATE_FINISH_COMPLETED = 6;
|
||||
public static final int STATE_FAILED = 7;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private final List<QueuedAsyncTask> mTaskQueue = new ArrayList<QueuedAsyncTask>();
|
||||
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
|
||||
|
||||
private final IPrintDocumentAdapter mRemoteInterface;
|
||||
|
||||
private final File mFile;
|
||||
|
||||
private int mState = STATE_INITIALIZED;
|
||||
|
||||
public RemotePrintDocumentAdapter(IPrintDocumentAdapter printAdatper, File file) {
|
||||
mRemoteInterface = printAdatper;
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
public void start() {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "getFile()");
|
||||
Log.i(LOG_TAG, "start()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (mState != STATE_WRITE_COMPLETED
|
||||
&& mState != STATE_FINISH_COMPLETED) {
|
||||
throw new IllegalStateException("Write not completed");
|
||||
}
|
||||
return mFile;
|
||||
try {
|
||||
mRemoteInterface.start();
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling start()", re);
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
QueuedAsyncTask task = new QueuedAsyncTask() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "start()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (mState != STATE_INITIALIZED) {
|
||||
throw new IllegalStateException("Invalid state: " + mState);
|
||||
}
|
||||
}
|
||||
try {
|
||||
mRemoteInterface.start();
|
||||
synchronized (mLock) {
|
||||
mState = STATE_START_COMPLETED;
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error reading file", re);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
/* cannot be cancelled */
|
||||
}
|
||||
};
|
||||
synchronized (mLock) {
|
||||
mTaskQueue.add(task);
|
||||
}
|
||||
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||
}
|
||||
|
||||
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
|
||||
LayoutResultCallback callback, Bundle metadata) {
|
||||
LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback,
|
||||
metadata);
|
||||
synchronized (mLock) {
|
||||
mTaskQueue.add(task);
|
||||
ILayoutResultCallback callback, Bundle metadata, int sequence) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "layout()");
|
||||
}
|
||||
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||
}
|
||||
|
||||
public void write(List<PageRange> pages, WriteResultCallback callback) {
|
||||
WriteAsyncTask task = new WriteAsyncTask(pages, callback);
|
||||
synchronized (mLock) {
|
||||
mTaskQueue.add(task);
|
||||
}
|
||||
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
synchronized (mLock) {
|
||||
final int taskCount = mTaskQueue.size();
|
||||
for (int i = taskCount - 1; i >= 0; i--) {
|
||||
mTaskQueue.remove(i).cancel();
|
||||
}
|
||||
try {
|
||||
mRemoteInterface.layout(oldAttributes, newAttributes, callback, metadata, sequence);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling layout()", re);
|
||||
}
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
QueuedAsyncTask task = new QueuedAsyncTask() {
|
||||
public void write(final PageRange[] pages, final IWriteResultCallback callback,
|
||||
final int sequence) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "write()");
|
||||
}
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "finish()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (mState < STATE_START_COMPLETED) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
ParcelFileDescriptor source = null;
|
||||
ParcelFileDescriptor sink = null;
|
||||
try {
|
||||
mRemoteInterface.finish();
|
||||
synchronized (mLock) {
|
||||
mState = STATE_FINISH_COMPLETED;
|
||||
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
|
||||
source = pipe[0];
|
||||
sink = pipe[1];
|
||||
|
||||
in = new FileInputStream(source.getFileDescriptor());
|
||||
out = new FileOutputStream(mFile);
|
||||
|
||||
// Async call to initiate the other process writing the data.
|
||||
mRemoteInterface.write(pages, sink, callback, sequence);
|
||||
|
||||
// Close the source. It is now held by the client.
|
||||
sink.close();
|
||||
sink = null;
|
||||
|
||||
// Read the data.
|
||||
final byte[] buffer = new byte[8192];
|
||||
while (true) {
|
||||
final int readByteCount = in.read(buffer);
|
||||
if (readByteCount < 0) {
|
||||
break;
|
||||
}
|
||||
out.write(buffer, 0, readByteCount);
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error reading file", re);
|
||||
mState = STATE_FAILED;
|
||||
Log.e(LOG_TAG, "Error calling write()", re);
|
||||
} catch (IOException ioe) {
|
||||
Log.e(LOG_TAG, "Error calling write()", ioe);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
IoUtils.closeQuietly(out);
|
||||
IoUtils.closeQuietly(sink);
|
||||
IoUtils.closeQuietly(source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
/* cannot be cancelled */
|
||||
}
|
||||
};
|
||||
synchronized (mLock) {
|
||||
mTaskQueue.add(task);
|
||||
}
|
||||
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||
}
|
||||
|
||||
private abstract class QueuedAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
public void cancel() {
|
||||
super.cancel(true);
|
||||
public void finish() {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "finish()");
|
||||
}
|
||||
}
|
||||
|
||||
private final class LayoutAsyncTask extends QueuedAsyncTask {
|
||||
|
||||
private final PrintAttributes mOldAttributes;
|
||||
|
||||
private final PrintAttributes mNewAttributes;
|
||||
|
||||
private final LayoutResultCallback mCallback;
|
||||
|
||||
private final Bundle mMetadata;
|
||||
|
||||
private final ILayoutResultCallback mILayoutResultCallback =
|
||||
new ILayoutResultCallback.Stub() {
|
||||
@Override
|
||||
public void onLayoutStarted(ICancellationSignal cancellationSignal) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onLayoutStarted()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mCancellationSignal = cancellationSignal;
|
||||
if (isCancelled()) {
|
||||
cancelSignalQuietlyLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onLayoutFinished()");
|
||||
}
|
||||
final boolean cancelled;
|
||||
synchronized (mLock) {
|
||||
cancelled = isCancelled();
|
||||
mCancellationSignal = null;
|
||||
mState = STATE_LAYOUT_COMPLETED;
|
||||
mTaskQueue.remove(this);
|
||||
mLock.notifyAll();
|
||||
}
|
||||
if (!cancelled) {
|
||||
mCallback.onLayoutFinished(info, changed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutFailed(CharSequence error) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onLayoutFailed()");
|
||||
}
|
||||
final boolean cancelled;
|
||||
synchronized (mLock) {
|
||||
cancelled = isCancelled();
|
||||
mCancellationSignal = null;
|
||||
mState = STATE_LAYOUT_COMPLETED;
|
||||
mTaskQueue.remove(this);
|
||||
mLock.notifyAll();
|
||||
}
|
||||
if (!cancelled) {
|
||||
mCallback.onLayoutFailed(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private ICancellationSignal mCancellationSignal;
|
||||
|
||||
public LayoutAsyncTask(PrintAttributes oldAttributes, PrintAttributes newAttributes,
|
||||
LayoutResultCallback callback, Bundle metadata) {
|
||||
mOldAttributes = oldAttributes;
|
||||
mNewAttributes = newAttributes;
|
||||
mCallback = callback;
|
||||
mMetadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
synchronized (mLock) {
|
||||
throwIfCancelledLocked();
|
||||
cancelSignalQuietlyLocked();
|
||||
}
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "layout()");
|
||||
}
|
||||
if (mState != STATE_START_COMPLETED
|
||||
&& mState != STATE_LAYOUT_COMPLETED
|
||||
&& mState != STATE_WRITE_COMPLETED) {
|
||||
throw new IllegalStateException("Invalid state: " + mState);
|
||||
}
|
||||
mState = STATE_LAYOUT_STARTED;
|
||||
}
|
||||
try {
|
||||
mRemoteInterface.layout(mOldAttributes, mNewAttributes,
|
||||
mILayoutResultCallback, mMetadata);
|
||||
synchronized (mLock) {
|
||||
while (true) {
|
||||
if (mState == STATE_LAYOUT_COMPLETED) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
mLock.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error calling layout", re);
|
||||
mState = STATE_FAILED;
|
||||
mTaskQueue.remove(this);
|
||||
notifyLayoutFailedQuietly();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cancelSignalQuietlyLocked() {
|
||||
if (mCancellationSignal != null) {
|
||||
try {
|
||||
mCancellationSignal.cancel();
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error cancelling layout", re);
|
||||
notifyLayoutFailedQuietly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyLayoutFailedQuietly() {
|
||||
try {
|
||||
mILayoutResultCallback.onLayoutFailed(null);
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
private void throwIfCancelledLocked() {
|
||||
if (isCancelled()) {
|
||||
throw new IllegalStateException("Already cancelled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class WriteAsyncTask extends QueuedAsyncTask {
|
||||
|
||||
private final List<PageRange> mPages;
|
||||
|
||||
private final WriteResultCallback mCallback;
|
||||
|
||||
private final IWriteResultCallback mIWriteResultCallback =
|
||||
new IWriteResultCallback.Stub() {
|
||||
@Override
|
||||
public void onWriteStarted(ICancellationSignal cancellationSignal) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onWriteStarted()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mCancellationSignal = cancellationSignal;
|
||||
if (isCancelled()) {
|
||||
cancelSignalQuietlyLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteFinished(List<PageRange> pages) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onWriteFinished()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mCancellationSignal = null;
|
||||
mState = STATE_WRITE_COMPLETED;
|
||||
mTaskQueue.remove(this);
|
||||
mLock.notifyAll();
|
||||
}
|
||||
mCallback.onWriteFinished(pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteFailed(CharSequence error) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onWriteFailed()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mCancellationSignal = null;
|
||||
mState = STATE_WRITE_COMPLETED;
|
||||
mTaskQueue.remove(this);
|
||||
mLock.notifyAll();
|
||||
}
|
||||
Slog.e(LOG_TAG, "Error writing print document: " + error);
|
||||
mCallback.onWriteFailed(error);
|
||||
}
|
||||
};
|
||||
|
||||
private ICancellationSignal mCancellationSignal;
|
||||
|
||||
private Thread mWriteThread;
|
||||
|
||||
public WriteAsyncTask(List<PageRange> pages, WriteResultCallback callback) {
|
||||
mPages = pages;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
synchronized (mLock) {
|
||||
throwIfCancelledLocked();
|
||||
cancelSignalQuietlyLocked();
|
||||
mWriteThread.interrupt();
|
||||
}
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "write()");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (mState != STATE_LAYOUT_COMPLETED
|
||||
&& mState != STATE_WRITE_COMPLETED) {
|
||||
throw new IllegalStateException("Invalid state: " + mState);
|
||||
}
|
||||
mState = STATE_WRITE_STARTED;
|
||||
}
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
ParcelFileDescriptor source = null;
|
||||
ParcelFileDescriptor sink = null;
|
||||
synchronized (mLock) {
|
||||
mWriteThread = Thread.currentThread();
|
||||
}
|
||||
try {
|
||||
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
|
||||
source = pipe[0];
|
||||
sink = pipe[1];
|
||||
|
||||
in = new FileInputStream(source.getFileDescriptor());
|
||||
out = new FileOutputStream(mFile);
|
||||
|
||||
// Async call to initiate the other process writing the data.
|
||||
mRemoteInterface.write(mPages, sink, mIWriteResultCallback);
|
||||
|
||||
// Close the source. It is now held by the client.
|
||||
sink.close();
|
||||
sink = null;
|
||||
|
||||
final byte[] buffer = new byte[8192];
|
||||
while (true) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
final int readByteCount = in.read(buffer);
|
||||
if (readByteCount < 0) {
|
||||
break;
|
||||
}
|
||||
out.write(buffer, 0, readByteCount);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
while (true) {
|
||||
if (mState == STATE_WRITE_COMPLETED) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
mLock.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error writing print document", re);
|
||||
mState = STATE_FAILED;
|
||||
mTaskQueue.remove(this);
|
||||
notifyWriteFailedQuietly();
|
||||
} catch (IOException ioe) {
|
||||
Slog.e(LOG_TAG, "Error writing print document", ioe);
|
||||
mState = STATE_FAILED;
|
||||
mTaskQueue.remove(this);
|
||||
notifyWriteFailedQuietly();
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
IoUtils.closeQuietly(out);
|
||||
IoUtils.closeQuietly(sink);
|
||||
IoUtils.closeQuietly(source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cancelSignalQuietlyLocked() {
|
||||
if (mCancellationSignal != null) {
|
||||
try {
|
||||
mCancellationSignal.cancel();
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(LOG_TAG, "Error cancelling layout", re);
|
||||
notifyWriteFailedQuietly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyWriteFailedQuietly() {
|
||||
try {
|
||||
mIWriteResultCallback.onWriteFailed(null);
|
||||
} catch (RemoteException re) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
private void throwIfCancelledLocked() {
|
||||
if (isCancelled()) {
|
||||
throw new IllegalStateException("Already cancelled");
|
||||
}
|
||||
try {
|
||||
mRemoteInterface.finish();
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error calling finish()", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
@@ -28,6 +29,7 @@ import android.os.Message;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
import android.print.IPrinterDiscoveryObserver;
|
||||
import android.print.PrintJobInfo;
|
||||
import android.print.PrintManager;
|
||||
@@ -46,11 +48,11 @@ import java.util.List;
|
||||
* and unbinding from the remote implementation. Clients can call methods of
|
||||
* this class without worrying about when and how to bind/unbind.
|
||||
*/
|
||||
final class RemotePrintService {
|
||||
final class RemotePrintService implements DeathRecipient {
|
||||
|
||||
private static final String LOG_TAG = "RemotePrintService";
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
@@ -101,6 +103,15 @@ final class RemotePrintService {
|
||||
mHandler.sendEmptyMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED);
|
||||
}
|
||||
|
||||
private void handleBinderDied() {
|
||||
ensureBound();
|
||||
}
|
||||
|
||||
private void handleOnAllPrintJobsHandled() {
|
||||
throwIfDestroyed();
|
||||
if (isBound()) {
|
||||
@@ -289,6 +300,7 @@ final class RemotePrintService {
|
||||
public static final int MSG_START_PRINTER_DISCOVERY = 4;
|
||||
public static final int MSG_STOP_PRINTER_DISCOVERY = 5;
|
||||
public static final int MSG_DESTROY = 6;
|
||||
public static final int MSG_BINDER_DIED = 7;
|
||||
|
||||
public MyHandler(Looper looper) {
|
||||
super(looper, null, false);
|
||||
@@ -324,6 +336,10 @@ final class RemotePrintService {
|
||||
case MSG_DESTROY: {
|
||||
handleDestroy();
|
||||
} break;
|
||||
|
||||
case MSG_BINDER_DIED: {
|
||||
handleBinderDied();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
@@ -54,7 +55,7 @@ final class RemotePrintSpooler {
|
||||
|
||||
private static final String LOG_TAG = "RemotePrintSpooler";
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
|
||||
|
||||
private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
|
||||
|
||||
@@ -88,6 +89,8 @@ final class RemotePrintSpooler {
|
||||
|
||||
private boolean mDestroyed;
|
||||
|
||||
private boolean mCanUnbind;
|
||||
|
||||
public static interface PrintSpoolerCallbacks {
|
||||
public void onPrintJobQueued(PrintJobInfo printJob);
|
||||
public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
|
||||
@@ -111,6 +114,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
|
||||
@@ -122,6 +126,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error getting print jobs.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error getting print jobs.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -131,6 +140,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
|
||||
@@ -142,6 +152,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error creating print job.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error creating print job.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -150,6 +165,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] cancelPrintJob()");
|
||||
@@ -161,6 +177,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error canceling print job.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error canceling print job.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -169,6 +190,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
|
||||
@@ -183,6 +205,10 @@ final class RemotePrintSpooler {
|
||||
// We passed the file descriptor across and now the other
|
||||
// side is responsible to close it, so close the local copy.
|
||||
IoUtils.closeQuietly(fd);
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +216,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
|
||||
@@ -201,6 +228,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error getting print job info.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error getting print job info.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -209,6 +241,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
|
||||
@@ -220,6 +253,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error setting print job state.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error setting print job state.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -228,6 +266,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
|
||||
@@ -239,6 +278,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error setting print job tag.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error setting print job tag.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -247,6 +291,7 @@ final class RemotePrintSpooler {
|
||||
throwIfCalledOnMainThread();
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
mCanUnbind = false;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
|
||||
@@ -258,6 +303,11 @@ final class RemotePrintSpooler {
|
||||
Slog.e(LOG_TAG, "Error asking for active print job notification.", re);
|
||||
} catch (TimeoutException te) {
|
||||
Slog.e(LOG_TAG, "Error asking for active print job notification.", te);
|
||||
} finally {
|
||||
synchronized (mLock) {
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +320,7 @@ final class RemotePrintSpooler {
|
||||
throwIfDestroyedLocked();
|
||||
unbindLocked();
|
||||
mDestroyed = true;
|
||||
mCanUnbind = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,15 +365,29 @@ final class RemotePrintSpooler {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
mCanUnbind = true;
|
||||
mLock.notifyAll();
|
||||
}
|
||||
|
||||
private void unbindLocked() {
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
|
||||
while (true) {
|
||||
if (mCanUnbind) {
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
|
||||
}
|
||||
clearClientLocked();
|
||||
mRemoteInstance = null;
|
||||
mContext.unbindService(mServiceConnection);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mLock.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
clearClientLocked();
|
||||
mRemoteInstance = null;
|
||||
mContext.unbindService(mServiceConnection);
|
||||
|
||||
}
|
||||
|
||||
private void setClientLocked() {
|
||||
|
||||
Reference in New Issue
Block a user