Merge "Implemented advanced printer selection and API refactoring." into klp-dev
This commit is contained in:
@@ -162,8 +162,6 @@ LOCAL_SRC_FILES += \
|
|||||||
core/java/android/os/IVibratorService.aidl \
|
core/java/android/os/IVibratorService.aidl \
|
||||||
core/java/android/service/notification/INotificationListener.aidl \
|
core/java/android/service/notification/INotificationListener.aidl \
|
||||||
core/java/android/print/ILayoutResultCallback.aidl \
|
core/java/android/print/ILayoutResultCallback.aidl \
|
||||||
core/java/android/print/IPrinterDiscoverySessionController.aidl \
|
|
||||||
core/java/android/print/IPrinterDiscoverySessionObserver.aidl \
|
|
||||||
core/java/android/print/IPrintDocumentAdapter.aidl \
|
core/java/android/print/IPrintDocumentAdapter.aidl \
|
||||||
core/java/android/print/IPrintClient.aidl \
|
core/java/android/print/IPrintClient.aidl \
|
||||||
core/java/android/print/IPrintManager.aidl \
|
core/java/android/print/IPrintManager.aidl \
|
||||||
|
|||||||
@@ -19038,7 +19038,7 @@ package android.print {
|
|||||||
method public android.print.PrintAttributes getAttributes();
|
method public android.print.PrintAttributes getAttributes();
|
||||||
method public int getCopies();
|
method public int getCopies();
|
||||||
method public int getId();
|
method public int getId();
|
||||||
method public java.lang.CharSequence getLabel();
|
method public java.lang.String getLabel();
|
||||||
method public android.print.PageRange[] getPages();
|
method public android.print.PageRange[] getPages();
|
||||||
method public android.print.PrinterId getPrinterId();
|
method public android.print.PrinterId getPrinterId();
|
||||||
method public int getState();
|
method public int getState();
|
||||||
@@ -19162,7 +19162,7 @@ package android.printservice {
|
|||||||
public final class PrintJob {
|
public final class PrintJob {
|
||||||
method public boolean cancel();
|
method public boolean cancel();
|
||||||
method public boolean complete();
|
method public boolean complete();
|
||||||
method public boolean fail(java.lang.CharSequence);
|
method public boolean fail(java.lang.String);
|
||||||
method public android.printservice.PrintDocument getDocument();
|
method public android.printservice.PrintDocument getDocument();
|
||||||
method public int getId();
|
method public int getId();
|
||||||
method public android.print.PrintJobInfo getInfo();
|
method public android.print.PrintJobInfo getInfo();
|
||||||
@@ -19191,11 +19191,15 @@ package android.printservice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract class PrinterDiscoverySession {
|
public abstract class PrinterDiscoverySession {
|
||||||
ctor public PrinterDiscoverySession(android.content.Context);
|
ctor public PrinterDiscoverySession();
|
||||||
method public final void addPrinters(java.util.List<android.print.PrinterInfo>);
|
method public final void addPrinters(java.util.List<android.print.PrinterInfo>);
|
||||||
method public abstract void onClose();
|
method public final java.util.List<android.print.PrinterInfo> getPrinters();
|
||||||
method public abstract void onOpen(java.util.List<android.print.PrinterId>);
|
method public final boolean isDestroyed();
|
||||||
|
method public final boolean isPrinterDiscoveryStarted();
|
||||||
|
method public abstract void onDestroy();
|
||||||
method public abstract void onRequestPrinterUpdate(android.print.PrinterId);
|
method public abstract void onRequestPrinterUpdate(android.print.PrinterId);
|
||||||
|
method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>);
|
||||||
|
method public abstract void onStopPrinterDiscovery();
|
||||||
method public final void removePrinters(java.util.List<android.print.PrinterId>);
|
method public final void removePrinters(java.util.List<android.print.PrinterId>);
|
||||||
method public final void updatePrinters(java.util.List<android.print.PrinterInfo>);
|
method public final void updatePrinters(java.util.List<android.print.PrinterInfo>);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.print;
|
|||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.print.PrinterId;
|
||||||
import android.print.IPrintDocumentAdapter;
|
import android.print.IPrintDocumentAdapter;
|
||||||
import android.print.IPrintClient;
|
import android.print.IPrintClient;
|
||||||
import android.print.IPrintSpoolerClient;
|
import android.print.IPrintSpoolerClient;
|
||||||
@@ -40,10 +41,15 @@ oneway interface IPrintSpooler {
|
|||||||
void createPrintJob(String printJobName, in IPrintClient client,
|
void createPrintJob(String printJobName, in IPrintClient client,
|
||||||
in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
|
in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
|
||||||
IPrintSpoolerCallbacks callback, int appId, int sequence);
|
IPrintSpoolerCallbacks callback, int appId, int sequence);
|
||||||
void setPrintJobState(int printJobId, int status, CharSequence error,
|
void setPrintJobState(int printJobId, int status, String error,
|
||||||
IPrintSpoolerCallbacks callback, int sequence);
|
IPrintSpoolerCallbacks callback, int sequence);
|
||||||
void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback,
|
void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback,
|
||||||
int sequence);
|
int sequence);
|
||||||
void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
|
void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
|
||||||
void setClient(IPrintSpoolerClient client);
|
void setClient(IPrintSpoolerClient client);
|
||||||
|
|
||||||
|
// Printer discovery APIs
|
||||||
|
void onPrintersAdded(in List<PrinterInfo> printers);
|
||||||
|
void onPrintersRemoved(in List<PrinterId> printerIds);
|
||||||
|
void onPrintersUpdated(in List<PrinterInfo> printerIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package android.print;
|
package android.print;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrinterId;
|
import android.print.PrinterId;
|
||||||
import android.print.PrintJobInfo;
|
import android.print.PrintJobInfo;
|
||||||
|
|
||||||
@@ -28,8 +27,14 @@ import android.print.PrintJobInfo;
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
oneway interface IPrintSpoolerClient {
|
oneway interface IPrintSpoolerClient {
|
||||||
void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
|
|
||||||
void onPrintJobQueued(in PrintJobInfo printJob);
|
void onPrintJobQueued(in PrintJobInfo printJob);
|
||||||
void onAllPrintJobsForServiceHandled(in ComponentName printService);
|
void onAllPrintJobsForServiceHandled(in ComponentName printService);
|
||||||
void onAllPrintJobsHandled();
|
void onAllPrintJobsHandled();
|
||||||
|
|
||||||
|
// Printer discovery APIs
|
||||||
|
void createPrinterDiscoverySession();
|
||||||
|
void startPrinterDiscovery(in List<PrinterId> priorityList);
|
||||||
|
void stopPrinterDiscovery();
|
||||||
|
void requestPrinterUpdate(in PrinterId printerId);
|
||||||
|
void destroyPrinterDiscoverySession();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for the controlling part of a printer discovery session.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
oneway interface IPrinterDiscoverySessionController {
|
|
||||||
void open(in List<PrinterId> priorityList);
|
|
||||||
void requestPrinterUpdate(in PrinterId printerId);
|
|
||||||
void close();
|
|
||||||
}
|
|
||||||
@@ -1,33 +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.IPrinterDiscoverySessionController;
|
|
||||||
import android.print.PrinterId;
|
|
||||||
import android.print.PrinterInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for the observing part of a printer discovery session.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
oneway interface IPrinterDiscoverySessionObserver {
|
|
||||||
void setController(IPrinterDiscoverySessionController controller);
|
|
||||||
void onPrintersAdded(in List<PrinterInfo> printers);
|
|
||||||
void onPrintersRemoved(in List<PrinterId> printerIds);
|
|
||||||
void onPrintersUpdated(in List<PrinterInfo> printerIds);
|
|
||||||
}
|
|
||||||
@@ -104,7 +104,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
private int mId;
|
private int mId;
|
||||||
|
|
||||||
/** The human readable print job label. */
|
/** The human readable print job label. */
|
||||||
private CharSequence mLabel;
|
private String mLabel;
|
||||||
|
|
||||||
/** The unique id of the printer. */
|
/** The unique id of the printer. */
|
||||||
private PrinterId mPrinterId;
|
private PrinterId mPrinterId;
|
||||||
@@ -128,7 +128,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
private int mCopies;
|
private int mCopies;
|
||||||
|
|
||||||
/** Failure reason if this job failed. */
|
/** Failure reason if this job failed. */
|
||||||
private CharSequence mFailureReason;
|
private String mFailureReason;
|
||||||
|
|
||||||
/** The pages to print */
|
/** The pages to print */
|
||||||
private PageRange[] mPageRanges;
|
private PageRange[] mPageRanges;
|
||||||
@@ -163,7 +163,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
|
|
||||||
private PrintJobInfo(Parcel parcel) {
|
private PrintJobInfo(Parcel parcel) {
|
||||||
mId = parcel.readInt();
|
mId = parcel.readInt();
|
||||||
mLabel = parcel.readCharSequence();
|
mLabel = parcel.readString();
|
||||||
mPrinterId = parcel.readParcelable(null);
|
mPrinterId = parcel.readParcelable(null);
|
||||||
mPrinterName = parcel.readString();
|
mPrinterName = parcel.readString();
|
||||||
mState = parcel.readInt();
|
mState = parcel.readInt();
|
||||||
@@ -171,9 +171,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
mUserId = parcel.readInt();
|
mUserId = parcel.readInt();
|
||||||
mTag = parcel.readString();
|
mTag = parcel.readString();
|
||||||
mCopies = parcel.readInt();
|
mCopies = parcel.readInt();
|
||||||
if (parcel.readInt() == 1) {
|
mFailureReason = parcel.readString();
|
||||||
mFailureReason = parcel.readCharSequence();
|
|
||||||
}
|
|
||||||
if (parcel.readInt() == 1) {
|
if (parcel.readInt() == 1) {
|
||||||
Parcelable[] parcelables = parcel.readParcelableArray(null);
|
Parcelable[] parcelables = parcel.readParcelableArray(null);
|
||||||
mPageRanges = new PageRange[parcelables.length];
|
mPageRanges = new PageRange[parcelables.length];
|
||||||
@@ -214,7 +212,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
*
|
*
|
||||||
* @return The label.
|
* @return The label.
|
||||||
*/
|
*/
|
||||||
public CharSequence getLabel() {
|
public String getLabel() {
|
||||||
return mLabel;
|
return mLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +223,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public void setLabel(CharSequence label) {
|
public void setLabel(String label) {
|
||||||
mLabel = label;
|
mLabel = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +383,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public CharSequence getFailureReason() {
|
public String getFailureReason() {
|
||||||
return mFailureReason;
|
return mFailureReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +394,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public void setFailureReason(CharSequence failureReason) {
|
public void setFailureReason(String failureReason) {
|
||||||
mFailureReason = failureReason;
|
mFailureReason = failureReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,7 +468,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel parcel, int flags) {
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
parcel.writeInt(mId);
|
parcel.writeInt(mId);
|
||||||
parcel.writeCharSequence(mLabel);
|
parcel.writeString(mLabel);
|
||||||
parcel.writeParcelable(mPrinterId, flags);
|
parcel.writeParcelable(mPrinterId, flags);
|
||||||
parcel.writeString(mPrinterName);
|
parcel.writeString(mPrinterName);
|
||||||
parcel.writeInt(mState);
|
parcel.writeInt(mState);
|
||||||
@@ -478,12 +476,7 @@ public final class PrintJobInfo implements Parcelable {
|
|||||||
parcel.writeInt(mUserId);
|
parcel.writeInt(mUserId);
|
||||||
parcel.writeString(mTag);
|
parcel.writeString(mTag);
|
||||||
parcel.writeInt(mCopies);
|
parcel.writeInt(mCopies);
|
||||||
if (mFailureReason != null) {
|
parcel.writeString(mFailureReason);
|
||||||
parcel.writeInt(1);
|
|
||||||
parcel.writeCharSequence(mFailureReason);
|
|
||||||
} else {
|
|
||||||
parcel.writeInt(0);
|
|
||||||
}
|
|
||||||
if (mPageRanges != null) {
|
if (mPageRanges != null) {
|
||||||
parcel.writeInt(1);
|
parcel.writeInt(1);
|
||||||
parcel.writeParcelableArray(mPageRanges, flags);
|
parcel.writeParcelableArray(mPageRanges, flags);
|
||||||
|
|||||||
@@ -374,14 +374,14 @@ public final class PrintManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
|
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
|
||||||
|
if (info == null) {
|
||||||
|
throw new NullPointerException("document info cannot be null");
|
||||||
|
}
|
||||||
final ILayoutResultCallback callback;
|
final ILayoutResultCallback callback;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
callback = mCallback;
|
callback = mCallback;
|
||||||
clearLocked();
|
clearLocked();
|
||||||
}
|
}
|
||||||
if (info == null) {
|
|
||||||
throw new IllegalArgumentException("info cannot be null");
|
|
||||||
}
|
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
try {
|
try {
|
||||||
callback.onLayoutFinished(info, changed, mSequence);
|
callback.onLayoutFinished(info, changed, mSequence);
|
||||||
|
|||||||
@@ -229,10 +229,11 @@ public final class PrinterInfo implements Parcelable {
|
|||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param prototype Prototype from which to start building.
|
* @param other Other info from which to start building.
|
||||||
*/
|
*/
|
||||||
public Builder(PrinterInfo prototype) {
|
public Builder(PrinterInfo other) {
|
||||||
mPrototype = prototype;
|
mPrototype = new PrinterInfo();
|
||||||
|
mPrototype.copyFrom(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ public final class PdfDocument {
|
|||||||
/**
|
/**
|
||||||
* Creates a new builder with the mandatory page info attributes.
|
* Creates a new builder with the mandatory page info attributes.
|
||||||
*
|
*
|
||||||
* @param pageSize The page size in pixels.
|
* @param pageSize The page size in points, <strong>not</strong> dips.
|
||||||
* @param pageNumber The page number.
|
* @param pageNumber The page number.
|
||||||
* @param density The page density in DPI.
|
* @param density The page density in DPI.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package android.printservice;
|
package android.printservice;
|
||||||
|
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
import android.print.PrinterId;
|
||||||
import android.print.PrintJobInfo;
|
import android.print.PrintJobInfo;
|
||||||
import android.printservice.IPrintServiceClient;
|
import android.printservice.IPrintServiceClient;
|
||||||
|
|
||||||
@@ -29,5 +29,10 @@ oneway interface IPrintService {
|
|||||||
void setClient(IPrintServiceClient client);
|
void setClient(IPrintServiceClient client);
|
||||||
void requestCancelPrintJob(in PrintJobInfo printJobInfo);
|
void requestCancelPrintJob(in PrintJobInfo printJobInfo);
|
||||||
void onPrintJobQueued(in PrintJobInfo printJobInfo);
|
void onPrintJobQueued(in PrintJobInfo printJobInfo);
|
||||||
void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
|
|
||||||
|
void createPrinterDiscoverySession();
|
||||||
|
void startPrinterDiscovery(in List<PrinterId> priorityList);
|
||||||
|
void stopPrinterDiscovery();
|
||||||
|
void requestPrinterUpdate(in PrinterId printerId);
|
||||||
|
void destroyPrinterDiscoverySession();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ import android.print.PrinterInfo;
|
|||||||
interface IPrintServiceClient {
|
interface IPrintServiceClient {
|
||||||
List<PrintJobInfo> getPrintJobInfos();
|
List<PrintJobInfo> getPrintJobInfos();
|
||||||
PrintJobInfo getPrintJobInfo(int printJobId);
|
PrintJobInfo getPrintJobInfo(int printJobId);
|
||||||
boolean setPrintJobState(int printJobId, int state, CharSequence error);
|
boolean setPrintJobState(int printJobId, int state, String error);
|
||||||
boolean setPrintJobTag(int printJobId, String tag);
|
boolean setPrintJobTag(int printJobId, String tag);
|
||||||
oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
|
oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
|
||||||
|
|
||||||
|
void onPrintersAdded(in List<PrinterInfo> printers);
|
||||||
|
void onPrintersRemoved(in List<PrinterId> printerIds);
|
||||||
|
void onPrintersUpdated(in List<PrinterInfo> printers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ import android.util.Log;
|
|||||||
* This class represents a print job from the perspective of a print
|
* This class represents a print job from the perspective of a print
|
||||||
* service. It provides APIs for observing the print job state and
|
* service. It provides APIs for observing the print job state and
|
||||||
* performing operations on the print job.
|
* performing operations on the print job.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note: </strong> All methods of this class must be executed on the main
|
||||||
|
* application thread.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public final class PrintJob {
|
public final class PrintJob {
|
||||||
|
|
||||||
@@ -48,6 +52,7 @@ public final class PrintJob {
|
|||||||
* @return The id.
|
* @return The id.
|
||||||
*/
|
*/
|
||||||
public int getId() {
|
public int getId() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return mCachedInfo.getId();
|
return mCachedInfo.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +67,7 @@ public final class PrintJob {
|
|||||||
* @return The print job info.
|
* @return The print job info.
|
||||||
*/
|
*/
|
||||||
public PrintJobInfo getInfo() {
|
public PrintJobInfo getInfo() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
if (isInImmutableState()) {
|
if (isInImmutableState()) {
|
||||||
return mCachedInfo;
|
return mCachedInfo;
|
||||||
}
|
}
|
||||||
@@ -83,6 +89,7 @@ public final class PrintJob {
|
|||||||
* @return The document.
|
* @return The document.
|
||||||
*/
|
*/
|
||||||
public PrintDocument getDocument() {
|
public PrintDocument getDocument() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return mDocument;
|
return mDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +103,7 @@ public final class PrintJob {
|
|||||||
* @see #cancel()
|
* @see #cancel()
|
||||||
*/
|
*/
|
||||||
public boolean isQueued() {
|
public boolean isQueued() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
|
return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +118,7 @@ public final class PrintJob {
|
|||||||
* @see #fail(CharSequence)
|
* @see #fail(CharSequence)
|
||||||
*/
|
*/
|
||||||
public boolean isStarted() {
|
public boolean isStarted() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return getInfo().getState() == PrintJobInfo.STATE_STARTED;
|
return getInfo().getState() == PrintJobInfo.STATE_STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +131,7 @@ public final class PrintJob {
|
|||||||
* @see #complete()
|
* @see #complete()
|
||||||
*/
|
*/
|
||||||
public boolean isCompleted() {
|
public boolean isCompleted() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return getInfo().getState() == PrintJobInfo.STATE_COMPLETED;
|
return getInfo().getState() == PrintJobInfo.STATE_COMPLETED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +144,7 @@ public final class PrintJob {
|
|||||||
* @see #fail(CharSequence)
|
* @see #fail(CharSequence)
|
||||||
*/
|
*/
|
||||||
public boolean isFailed() {
|
public boolean isFailed() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
|
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +157,7 @@ public final class PrintJob {
|
|||||||
* @see #cancel()
|
* @see #cancel()
|
||||||
*/
|
*/
|
||||||
public boolean isCancelled() {
|
public boolean isCancelled() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
|
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +170,7 @@ public final class PrintJob {
|
|||||||
* @see #isQueued()
|
* @see #isQueued()
|
||||||
*/
|
*/
|
||||||
public boolean start() {
|
public boolean start() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
if (isQueued()) {
|
if (isQueued()) {
|
||||||
return setState(PrintJobInfo.STATE_STARTED, null);
|
return setState(PrintJobInfo.STATE_STARTED, null);
|
||||||
}
|
}
|
||||||
@@ -173,6 +186,7 @@ public final class PrintJob {
|
|||||||
* @see #isStarted()
|
* @see #isStarted()
|
||||||
*/
|
*/
|
||||||
public boolean complete() {
|
public boolean complete() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
if (isStarted()) {
|
if (isStarted()) {
|
||||||
return setState(PrintJobInfo.STATE_COMPLETED, null);
|
return setState(PrintJobInfo.STATE_COMPLETED, null);
|
||||||
}
|
}
|
||||||
@@ -191,7 +205,8 @@ public final class PrintJob {
|
|||||||
* @see #isQueued()
|
* @see #isQueued()
|
||||||
* @see #isStarted()
|
* @see #isStarted()
|
||||||
*/
|
*/
|
||||||
public boolean fail(CharSequence error) {
|
public boolean fail(String error) {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
if (isQueued() || isStarted()) {
|
if (isQueued() || isStarted()) {
|
||||||
return setState(PrintJobInfo.STATE_FAILED, error);
|
return setState(PrintJobInfo.STATE_FAILED, error);
|
||||||
}
|
}
|
||||||
@@ -210,6 +225,7 @@ public final class PrintJob {
|
|||||||
* @see #isQueued()
|
* @see #isQueued()
|
||||||
*/
|
*/
|
||||||
public boolean cancel() {
|
public boolean cancel() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
if (isQueued() || isStarted()) {
|
if (isQueued() || isStarted()) {
|
||||||
return setState(PrintJobInfo.STATE_CANCELED, null);
|
return setState(PrintJobInfo.STATE_CANCELED, null);
|
||||||
}
|
}
|
||||||
@@ -226,6 +242,7 @@ public final class PrintJob {
|
|||||||
* @return True if the tag was set, false otherwise.
|
* @return True if the tag was set, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean setTag(String tag) {
|
public boolean setTag(String tag) {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
if (isInImmutableState()) {
|
if (isInImmutableState()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -263,7 +280,7 @@ public final class PrintJob {
|
|||||||
|| state == PrintJobInfo.STATE_CANCELED;
|
|| state == PrintJobInfo.STATE_CANCELED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean setState(int state, CharSequence error) {
|
private boolean setState(int state, String error) {
|
||||||
try {
|
try {
|
||||||
if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) {
|
if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) {
|
||||||
// Best effort - update the state of the cached info since
|
// Best effort - update the state of the cached info since
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.os.IBinder;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrintJobInfo;
|
import android.print.PrintJobInfo;
|
||||||
import android.print.PrinterId;
|
import android.print.PrinterId;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -146,6 +145,11 @@ import java.util.List;
|
|||||||
* {@link #SERVICE_META_DATA} and <code><{@link android.R.styleable#PrintService
|
* {@link #SERVICE_META_DATA} and <code><{@link android.R.styleable#PrintService
|
||||||
* print-service}></code>.
|
* print-service}></code>.
|
||||||
* </p>
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* <strong>Note: </strong> All callbacks in this class are executed on the main
|
||||||
|
* application thread. You should also invoke any method of this class on the main
|
||||||
|
* application thread.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public abstract class PrintService extends Service {
|
public abstract class PrintService extends Service {
|
||||||
|
|
||||||
@@ -175,14 +179,14 @@ public abstract class PrintService extends Service {
|
|||||||
*/
|
*/
|
||||||
public static final String SERVICE_META_DATA = "android.printservice";
|
public static final String SERVICE_META_DATA = "android.printservice";
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
|
||||||
|
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
|
|
||||||
private IPrintServiceClient mClient;
|
private IPrintServiceClient mClient;
|
||||||
|
|
||||||
private int mLastSessionId = -1;
|
private int mLastSessionId = -1;
|
||||||
|
|
||||||
|
private PrinterDiscoverySession mDiscoverySession;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void attachBaseContext(Context base) {
|
protected final void attachBaseContext(Context base) {
|
||||||
super.attachBaseContext(base);
|
super.attachBaseContext(base);
|
||||||
@@ -245,21 +249,18 @@ public abstract class PrintService extends Service {
|
|||||||
* @see PrintJob#isStarted() PrintJob.isStarted()
|
* @see PrintJob#isStarted() PrintJob.isStarted()
|
||||||
*/
|
*/
|
||||||
public final List<PrintJob> getActivePrintJobs() {
|
public final List<PrintJob> getActivePrintJobs() {
|
||||||
final IPrintServiceClient client;
|
throwIfNotCalledOnMainThread();
|
||||||
synchronized (mLock) {
|
if (mClient == null) {
|
||||||
client = mClient;
|
|
||||||
}
|
|
||||||
if (client == null) {
|
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
List<PrintJob> printJobs = null;
|
List<PrintJob> printJobs = null;
|
||||||
List<PrintJobInfo> printJobInfos = client.getPrintJobInfos();
|
List<PrintJobInfo> printJobInfos = mClient.getPrintJobInfos();
|
||||||
if (printJobInfos != null) {
|
if (printJobInfos != null) {
|
||||||
final int printJobInfoCount = printJobInfos.size();
|
final int printJobInfoCount = printJobInfos.size();
|
||||||
printJobs = new ArrayList<PrintJob>(printJobInfoCount);
|
printJobs = new ArrayList<PrintJob>(printJobInfoCount);
|
||||||
for (int i = 0; i < printJobInfoCount; i++) {
|
for (int i = 0; i < printJobInfoCount; i++) {
|
||||||
printJobs.add(new PrintJob(printJobInfos.get(i), client));
|
printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (printJobs != null) {
|
if (printJobs != null) {
|
||||||
@@ -278,23 +279,50 @@ public abstract class PrintService extends Service {
|
|||||||
* @return Global printer id.
|
* @return Global printer id.
|
||||||
*/
|
*/
|
||||||
public final PrinterId generatePrinterId(String localId) {
|
public final PrinterId generatePrinterId(String localId) {
|
||||||
|
throwIfNotCalledOnMainThread();
|
||||||
return new PrinterId(new ComponentName(getPackageName(),
|
return new PrinterId(new ComponentName(getPackageName(),
|
||||||
getClass().getName()), localId);
|
getClass().getName()), localId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void throwIfNotCalledOnMainThread() {
|
||||||
|
if (!Looper.getMainLooper().isCurrentThread()) {
|
||||||
|
throw new IllegalAccessError("must be called from the main thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final IBinder onBind(Intent intent) {
|
public final IBinder onBind(Intent intent) {
|
||||||
return new IPrintService.Stub() {
|
return new IPrintService.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void setClient(IPrintServiceClient client) {
|
public void createPrinterDiscoverySession() {
|
||||||
mHandler.obtainMessage(ServiceHandler.MSG_SET_CLEINT, client)
|
mHandler.sendEmptyMessage(ServiceHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
|
||||||
.sendToTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
|
public void destroyPrinterDiscoverySession() {
|
||||||
mHandler.obtainMessage(ServiceHandler.MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION,
|
mHandler.sendEmptyMessage(ServiceHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
|
||||||
observer).sendToTarget();
|
}
|
||||||
|
|
||||||
|
public void startPrinterDiscovery(List<PrinterId> priorityList) {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_START_PRINTER_DISCOVERY,
|
||||||
|
priorityList).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopPrinterDiscovery() {
|
||||||
|
mHandler.sendEmptyMessage(ServiceHandler.MSG_STOP_PRINTER_DISCOVERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestPrinterUpdate(PrinterId printerId) {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_REQUEST_PRINTER_UPDATE,
|
||||||
|
printerId).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClient(IPrintServiceClient client) {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_SET_CLEINT, client)
|
||||||
|
.sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -312,33 +340,62 @@ public abstract class PrintService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class ServiceHandler extends Handler {
|
private final class ServiceHandler extends Handler {
|
||||||
public static final int MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION = 1;
|
public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
|
||||||
public static final int MSG_ON_PRINTJOB_QUEUED = 2;
|
public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
|
||||||
public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 3;
|
public static final int MSG_START_PRINTER_DISCOVERY = 3;
|
||||||
public static final int MSG_SET_CLEINT = 4;
|
public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
|
||||||
|
public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
|
||||||
|
public static final int MSG_ON_PRINTJOB_QUEUED = 6;
|
||||||
|
public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 7;
|
||||||
|
public static final int MSG_SET_CLEINT = 8;
|
||||||
|
|
||||||
public ServiceHandler(Looper looper) {
|
public ServiceHandler(Looper looper) {
|
||||||
super(looper, null, true);
|
super(looper, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
final int action = message.what;
|
final int action = message.what;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION: {
|
case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
|
||||||
IPrinterDiscoverySessionObserver observer =
|
|
||||||
(IPrinterDiscoverySessionObserver) message.obj;
|
|
||||||
PrinterDiscoverySession session = onCreatePrinterDiscoverySession();
|
PrinterDiscoverySession session = onCreatePrinterDiscoverySession();
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
throw new NullPointerException("session cannot be null");
|
throw new NullPointerException("session cannot be null");
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
if (session.getId() == mLastSessionId) {
|
||||||
if (session.getId() == mLastSessionId) {
|
throw new IllegalStateException("cannot reuse session instances");
|
||||||
throw new IllegalStateException("cannot reuse sessions");
|
}
|
||||||
}
|
mDiscoverySession = session;
|
||||||
mLastSessionId = session.getId();
|
mLastSessionId = session.getId();
|
||||||
|
session.setObserver(mClient);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
|
||||||
|
if (mDiscoverySession != null) {
|
||||||
|
mDiscoverySession.destroy();
|
||||||
|
mDiscoverySession = null;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_START_PRINTER_DISCOVERY: {
|
||||||
|
if (mDiscoverySession != null) {
|
||||||
|
List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj;
|
||||||
|
mDiscoverySession.startPrinterDiscovery(priorityList);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_STOP_PRINTER_DISCOVERY: {
|
||||||
|
if (mDiscoverySession != null) {
|
||||||
|
mDiscoverySession.stopPrinterDiscovery();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_REQUEST_PRINTER_UPDATE: {
|
||||||
|
if (mDiscoverySession != null) {
|
||||||
|
PrinterId printerId = (PrinterId) message.obj;
|
||||||
|
mDiscoverySession.requestPrinterUpdate(printerId);
|
||||||
}
|
}
|
||||||
session.setObserver(observer);
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case MSG_ON_REQUEST_CANCEL_PRINTJOB: {
|
case MSG_ON_REQUEST_CANCEL_PRINTJOB: {
|
||||||
@@ -352,15 +409,12 @@ public abstract class PrintService extends Service {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case MSG_SET_CLEINT: {
|
case MSG_SET_CLEINT: {
|
||||||
IPrintServiceClient client = (IPrintServiceClient) message.obj;
|
mClient = (IPrintServiceClient) message.obj;
|
||||||
synchronized (mLock) {
|
if (mClient != null) {
|
||||||
mClient = client;
|
|
||||||
}
|
|
||||||
if (client != null) {
|
|
||||||
onConnected();
|
onConnected();
|
||||||
} else {
|
} else {
|
||||||
onDisconnected();
|
onDisconnected();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
|||||||
@@ -16,18 +16,15 @@
|
|||||||
|
|
||||||
package android.printservice;
|
package android.printservice;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.print.IPrinterDiscoverySessionController;
|
import android.print.PrinterCapabilitiesInfo;
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrinterId;
|
import android.print.PrinterId;
|
||||||
import android.print.PrinterInfo;
|
import android.print.PrinterInfo;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,67 +33,75 @@ import java.util.List;
|
|||||||
* for adding discovered printers, removing already added printers that
|
* for adding discovered printers, removing already added printers that
|
||||||
* disappeared, and updating already added printers.
|
* disappeared, and updating already added printers.
|
||||||
* <p>
|
* <p>
|
||||||
* The opening of the session is announced by a call to {@link
|
* During the lifetime of this session you may be asked to start and stop
|
||||||
* PrinterDiscoverySession#onOpen(List)} at which point you should start printer
|
* performing printer discovery multiple times. You will receive a call to {@link
|
||||||
* discovery. The closing of the session is announced by a call to {@link
|
* PrinterDiscoverySession#onStartPrinterDiscovery(List)} to start printer
|
||||||
* PrinterDiscoverySession#onClose()} at which point you should stop printer
|
* discovery and a call to {@link PrinterDiscoverySession#onStopPrinterDiscovery()}
|
||||||
* discovery. Discovered printers are added by invoking {@link
|
* to stop printer discovery. When the system is no longer interested in printers
|
||||||
* PrinterDiscoverySession#addPrinters(List)}. Added printers that disappeared
|
* discovered by this session you will receive a call to {@link #onDestroy()} at
|
||||||
* are removed by invoking {@link PrinterDiscoverySession#removePrinters(List)}.
|
* which point the system will no longer call into the session and all the session
|
||||||
* Added printers whose properties or capabilities changed are updated through
|
* methods will do nothing.
|
||||||
* a call to {@link PrinterDiscoverySession#updatePrinters(List)}.
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Discovered printers are added by invoking {@link
|
||||||
|
* PrinterDiscoverySession#addPrinters(List)}. Added printers that disappeared are
|
||||||
|
* removed by invoking {@link PrinterDiscoverySession#removePrinters(List)}. Added
|
||||||
|
* printers whose properties or capabilities changed are updated through a call to
|
||||||
|
* {@link PrinterDiscoverySession#updatePrinters(List)}. The printers added in this
|
||||||
|
* session can be acquired via {@link #getPrinters()} where the returned printers
|
||||||
|
* will be an up-to-date snapshot of the printers that you reported during the
|
||||||
|
* session. Printers are <strong>not</strong> persisted across sessions.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* The system will make a call to
|
* The system will make a call to
|
||||||
* {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
|
* {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
|
||||||
* need to update a given printer. It is possible that you add a printer without
|
* need to update a given printer. It is possible that you add a printer without
|
||||||
* specifying its capabilities. This enables you to avoid querying all
|
* specifying its capabilities. This enables you to avoid querying all discovered
|
||||||
* discovered printers for their capabilities, rather querying the capabilities
|
* printers for their capabilities, rather querying the capabilities of a printer
|
||||||
* of a printer only if necessary. For example, the system will require that you
|
* only if necessary. For example, the system will request that you update a printer
|
||||||
* update a printer if it gets selected by the user. If you did not report the
|
* if it gets selected by the user. If you did not report the printer capabilities
|
||||||
* printer capabilities when adding it, you must do so after the system requests
|
* when adding it, you must do so after the system requests a printer update.
|
||||||
* a printer update. Otherwise, the printer will be ignored.
|
* Otherwise, the printer will be ignored.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* During printer discovery all printers that are known to your print service
|
* <strong>Note: </strong> All callbacks in this class are executed on the main
|
||||||
* have to be added. The system does not retain any printers from previous
|
* application thread. You also have to invoke any method of this class on the main
|
||||||
* sessions.
|
* application thread.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public abstract class PrinterDiscoverySession {
|
public abstract class PrinterDiscoverySession {
|
||||||
private static final String LOG_TAG = "PrinterDiscoverySession";
|
private static final String LOG_TAG = "PrinterDiscoverySession";
|
||||||
|
|
||||||
|
private static final int MAX_ITEMS_PER_CALLBACK = 100;
|
||||||
|
|
||||||
private static int sIdCounter = 0;
|
private static int sIdCounter = 0;
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
|
||||||
|
|
||||||
private final Handler mHandler;
|
|
||||||
|
|
||||||
private final int mId;
|
private final int mId;
|
||||||
|
|
||||||
private IPrinterDiscoverySessionController mController;
|
private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
|
||||||
|
new ArrayMap<PrinterId, PrinterInfo>();
|
||||||
|
|
||||||
private IPrinterDiscoverySessionObserver mObserver;
|
private ArrayMap<PrinterId, PrinterInfo> mLastSentPrinters;
|
||||||
|
|
||||||
|
private IPrintServiceClient mObserver;
|
||||||
|
|
||||||
|
private boolean mIsDestroyed;
|
||||||
|
|
||||||
|
private boolean mIsDiscoveryStarted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
|
||||||
* @param context A context instance.
|
|
||||||
*/
|
*/
|
||||||
public PrinterDiscoverySession(Context context) {
|
public PrinterDiscoverySession() {
|
||||||
mId = sIdCounter++;
|
mId = sIdCounter++;
|
||||||
mHandler = new SessionHandler(context.getMainLooper());
|
|
||||||
mController = new PrinterDiscoverySessionController(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setObserver(IPrinterDiscoverySessionObserver observer) {
|
void setObserver(IPrintServiceClient observer) {
|
||||||
synchronized (mLock) {
|
mObserver = observer;
|
||||||
mObserver = observer;
|
// If some printers were added in the method that
|
||||||
try {
|
// created the session, send them over.
|
||||||
mObserver.setController(mController);
|
if (!mPrinters.isEmpty()) {
|
||||||
} catch (RemoteException re) {
|
sendAddedPrinters(mObserver, getPrinters());
|
||||||
Log.e(LOG_TAG, "Error setting session controller", re);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,132 +109,358 @@ public abstract class PrinterDiscoverySession {
|
|||||||
return mId;
|
return mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the printers reported in this session. For example, if you add two
|
||||||
|
* printers and remove one of them, the returned list will contain only
|
||||||
|
* the printer that was added but not removed.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note: </strong> Calls to this method after the session is
|
||||||
|
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return The printers.
|
||||||
|
*
|
||||||
|
* @see #addPrinters(List)
|
||||||
|
* @see #removePrinters(List)
|
||||||
|
* @see #updatePrinters(List)
|
||||||
|
* @see #isDestroyed()
|
||||||
|
*/
|
||||||
|
public final List<PrinterInfo> getPrinters() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
|
if (mIsDestroyed) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return new ArrayList<PrinterInfo>(mPrinters.values());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds discovered printers. Adding an already added printer has no effect.
|
* Adds discovered printers. Adding an already added printer has no effect.
|
||||||
* Removed printers can be added again. You can call this method multiple
|
* Removed printers can be added again. You can call this method multiple
|
||||||
* times during printer discovery.
|
* times during the life of this session. Duplicates will be ignored.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Note: </strong> Calls to this method before the session is opened,
|
* <strong>Note: </strong> Calls to this method after the session is
|
||||||
* i.e. before the {@link #onOpen(List)} call, and after the session is closed,
|
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
|
||||||
* i.e. after the call to {@link #onClose()}, will be ignored.
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param printers The printers to add.
|
* @param printers The printers to add.
|
||||||
*
|
*
|
||||||
* @see #removePrinters(List)
|
* @see #removePrinters(List)
|
||||||
* @see #updatePrinters(List)
|
* @see #updatePrinters(List)
|
||||||
|
* @see #getPrinters()
|
||||||
|
* @see #isDestroyed()
|
||||||
*/
|
*/
|
||||||
public final void addPrinters(List<PrinterInfo> printers) {
|
public final void addPrinters(List<PrinterInfo> printers) {
|
||||||
final IPrinterDiscoverySessionObserver observer;
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
synchronized (mLock) {
|
|
||||||
observer = mObserver;
|
// If the session is destroyed - nothing do to.
|
||||||
|
if (mIsDestroyed) {
|
||||||
|
Log.w(LOG_TAG, "Not adding printers - session destroyed.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (observer != null) {
|
|
||||||
try {
|
if (mIsDiscoveryStarted) {
|
||||||
observer.onPrintersAdded(printers);
|
// If during discovery, add the new printers and send them.
|
||||||
} catch (RemoteException re) {
|
List<PrinterInfo> addedPrinters = new ArrayList<PrinterInfo>();
|
||||||
Log.e(LOG_TAG, "Error adding printers", re);
|
final int addedPrinterCount = printers.size();
|
||||||
|
for (int i = 0; i < addedPrinterCount; i++) {
|
||||||
|
PrinterInfo addedPrinter = printers.get(i);
|
||||||
|
if (mPrinters.get(addedPrinter.getId()) == null) {
|
||||||
|
mPrinters.put(addedPrinter.getId(), addedPrinter);
|
||||||
|
addedPrinters.add(addedPrinter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the added printers, if such.
|
||||||
|
if (!addedPrinters.isEmpty()) {
|
||||||
|
sendAddedPrinters(mObserver, addedPrinters);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(LOG_TAG, "Printer discovery session not open not adding printers.");
|
// Remember the last sent printers if needed.
|
||||||
|
if (mLastSentPrinters == null) {
|
||||||
|
mLastSentPrinters = new ArrayMap<PrinterId, PrinterInfo>(mPrinters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the printers.
|
||||||
|
final int addedPrinterCount = printers.size();
|
||||||
|
for (int i = 0; i < addedPrinterCount; i++) {
|
||||||
|
PrinterInfo addedPrinter = printers.get(i);
|
||||||
|
if (mPrinters.get(addedPrinter.getId()) == null) {
|
||||||
|
mPrinters.put(addedPrinter.getId(), addedPrinter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendAddedPrinters(IPrintServiceClient observer,
|
||||||
|
List<PrinterInfo> printers) {
|
||||||
|
try {
|
||||||
|
final int printerCount = printers.size();
|
||||||
|
if (printerCount <= MAX_ITEMS_PER_CALLBACK) {
|
||||||
|
observer.onPrintersAdded(printers);
|
||||||
|
} else {
|
||||||
|
// Send the added printers in chunks avoiding the binder transaction limit.
|
||||||
|
final int transactionCount = (printerCount / MAX_ITEMS_PER_CALLBACK) + 1;
|
||||||
|
for (int i = 0; i < transactionCount; i++) {
|
||||||
|
final int start = i * MAX_ITEMS_PER_CALLBACK;
|
||||||
|
final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount);
|
||||||
|
List<PrinterInfo> subPrinters = printers.subList(start, end);
|
||||||
|
observer.onPrintersAdded(subPrinters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Log.e(LOG_TAG, "Error sending added printers", re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes added printers. Removing an already removed or never added
|
* Removes added printers. Removing an already removed or never added
|
||||||
* printer has no effect. Removed printers can be added again. You
|
* printer has no effect. Removed printers can be added again. You can
|
||||||
* can call this method multiple times during printer discovery.
|
* call this method multiple times during the lifetime of this session.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Note: </strong> Calls to this method before the session is opened,
|
* <strong>Note: </strong> Calls to this method after the session is
|
||||||
* i.e. before the {@link #onOpen(List)} call, and after the session is closed,
|
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
|
||||||
* i.e. after the call to {@link #onClose()}, will be ignored.
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param printerIds The ids of the removed printers.
|
* @param printerIds The ids of the removed printers.
|
||||||
*
|
*
|
||||||
* @see #addPrinters(List)
|
* @see #addPrinters(List)
|
||||||
* @see #updatePrinters(List)
|
* @see #updatePrinters(List)
|
||||||
|
* @see #getPrinters()
|
||||||
|
* @see #isDestroyed()
|
||||||
*/
|
*/
|
||||||
public final void removePrinters(List<PrinterId> printerIds) {
|
public final void removePrinters(List<PrinterId> printerIds) {
|
||||||
final IPrinterDiscoverySessionObserver observer;
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
synchronized (mLock) {
|
|
||||||
observer = mObserver;
|
// If the session is destroyed - nothing do to.
|
||||||
|
if (mIsDestroyed) {
|
||||||
|
Log.w(LOG_TAG, "Not removing printers - session destroyed.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (observer != null) {
|
|
||||||
try {
|
if (mIsDiscoveryStarted) {
|
||||||
observer.onPrintersRemoved(printerIds);
|
// If during discovery, remove existing printers and send them.
|
||||||
} catch (RemoteException re) {
|
List<PrinterId> removedPrinterIds = new ArrayList<PrinterId>();
|
||||||
Log.e(LOG_TAG, "Error removing printers", re);
|
final int removedPrinterIdCount = printerIds.size();
|
||||||
|
for (int i = 0; i < removedPrinterIdCount; i++) {
|
||||||
|
PrinterId removedPrinterId = printerIds.get(i);
|
||||||
|
if (mPrinters.remove(removedPrinterId) != null) {
|
||||||
|
removedPrinterIds.add(removedPrinterId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the removed printers, if such.
|
||||||
|
if (!removedPrinterIds.isEmpty()) {
|
||||||
|
sendRemovedPrinters(mObserver, removedPrinterIds);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(LOG_TAG, "Printer discovery session not open not removing printers.");
|
// Remember the last sent printers if needed.
|
||||||
|
if (mLastSentPrinters == null) {
|
||||||
|
mLastSentPrinters = new ArrayMap<PrinterId, PrinterInfo>(mPrinters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the printers.
|
||||||
|
final int removedPrinterIdCount = printerIds.size();
|
||||||
|
for (int i = 0; i < removedPrinterIdCount; i++) {
|
||||||
|
PrinterId removedPrinterId = printerIds.get(i);
|
||||||
|
mPrinters.remove(removedPrinterId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendRemovedPrinters(IPrintServiceClient observer,
|
||||||
|
List<PrinterId> printerIds) {
|
||||||
|
try {
|
||||||
|
final int printerIdCount = printerIds.size();
|
||||||
|
if (printerIdCount <= MAX_ITEMS_PER_CALLBACK) {
|
||||||
|
observer.onPrintersRemoved(printerIds);
|
||||||
|
} else {
|
||||||
|
final int transactionCount = (printerIdCount / MAX_ITEMS_PER_CALLBACK) + 1;
|
||||||
|
for (int i = 0; i < transactionCount; i++) {
|
||||||
|
final int start = i * MAX_ITEMS_PER_CALLBACK;
|
||||||
|
final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerIdCount);
|
||||||
|
List<PrinterId> subPrinterIds = printerIds.subList(start, end);
|
||||||
|
observer.onPrintersRemoved(subPrinterIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Log.e(LOG_TAG, "Error sending removed printers", re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates added printers. Updating a printer that was not added or that
|
* Updates added printers. Updating a printer that was not added or that
|
||||||
* was removed has no effect. You can call this method multiple times
|
* was removed has no effect. You can call this method multiple times
|
||||||
* during printer discovery.
|
* during the lifetime of this session.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Note: </strong> Calls to this method before the session is opened,
|
* <strong>Note: </strong> Calls to this method after the session is
|
||||||
* i.e. before the {@link #onOpen(List)} call, and after the session is closed,
|
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
|
||||||
* i.e. after the call to {@link #onClose()}, will be ignored.
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param printers The printers to update.
|
* @param printers The printers to update.
|
||||||
*
|
*
|
||||||
* @see #addPrinters(List)
|
* @see #addPrinters(List)
|
||||||
* @see #removePrinters(List)
|
* @see #removePrinters(List)
|
||||||
|
* @see #getPrinters()
|
||||||
|
* @see #isDestroyed()
|
||||||
*/
|
*/
|
||||||
public final void updatePrinters(List<PrinterInfo> printers) {
|
public final void updatePrinters(List<PrinterInfo> printers) {
|
||||||
final IPrinterDiscoverySessionObserver observer;
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
synchronized (mLock) {
|
|
||||||
observer = mObserver;
|
// If the session is destroyed - nothing do to.
|
||||||
|
if (mIsDestroyed) {
|
||||||
|
Log.w(LOG_TAG, "Not updating printers - session destroyed.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (observer != null) {
|
|
||||||
try {
|
if (mIsDiscoveryStarted) {
|
||||||
observer.onPrintersUpdated(printers);
|
// If during discovery, update existing printers and send them.
|
||||||
} catch (RemoteException re) {
|
List<PrinterInfo> updatedPrinters = new ArrayList<PrinterInfo>();
|
||||||
Log.e(LOG_TAG, "Error updating printers", re);
|
final int updatedPrinterCount = printers.size();
|
||||||
|
for (int i = 0; i < updatedPrinterCount; i++) {
|
||||||
|
PrinterInfo updatedPrinter = printers.get(i);
|
||||||
|
PrinterInfo oldPrinter = mPrinters.get(updatedPrinter.getId());
|
||||||
|
if (oldPrinter != null && !oldPrinter.equals(updatedPrinter)) {
|
||||||
|
mPrinters.put(updatedPrinter.getId(), updatedPrinter);
|
||||||
|
updatedPrinters.add(updatedPrinter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the updated printers, if such.
|
||||||
|
if (!updatedPrinters.isEmpty()) {
|
||||||
|
sendUpdatedPrinters(mObserver, updatedPrinters);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(LOG_TAG, "Printer discovery session not open not updating printers.");
|
// Remember the last sent printers if needed.
|
||||||
|
if (mLastSentPrinters == null) {
|
||||||
|
mLastSentPrinters = new ArrayMap<PrinterId, PrinterInfo>(mPrinters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the printers.
|
||||||
|
final int updatedPrinterCount = printers.size();
|
||||||
|
for (int i = 0; i < updatedPrinterCount; i++) {
|
||||||
|
PrinterInfo updatedPrinter = printers.get(i);
|
||||||
|
PrinterInfo oldPrinter = mPrinters.get(updatedPrinter.getId());
|
||||||
|
if (oldPrinter != null && !oldPrinter.equals(updatedPrinter)) {
|
||||||
|
mPrinters.put(updatedPrinter.getId(), updatedPrinter);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void sendUpdatedPrinters(IPrintServiceClient observer,
|
||||||
|
List<PrinterInfo> printers) {
|
||||||
|
try {
|
||||||
|
final int printerCount = printers.size();
|
||||||
|
if (printerCount <= MAX_ITEMS_PER_CALLBACK) {
|
||||||
|
observer.onPrintersUpdated(printers);
|
||||||
|
} else {
|
||||||
|
final int transactionCount = (printerCount / MAX_ITEMS_PER_CALLBACK) + 1;
|
||||||
|
for (int i = 0; i < transactionCount; i++) {
|
||||||
|
final int start = i * MAX_ITEMS_PER_CALLBACK;
|
||||||
|
final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount);
|
||||||
|
List<PrinterInfo> subPrinters = printers.subList(start, end);
|
||||||
|
observer.onPrintersUpdated(subPrinters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Log.e(LOG_TAG, "Error sending updated printers", re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendOutOfDiscoveryPeriodPrinterChanges() {
|
||||||
|
// Noting changed since the last discovery period - nothing to do.
|
||||||
|
if (mLastSentPrinters == null || mLastSentPrinters.isEmpty()) {
|
||||||
|
mLastSentPrinters = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PrinterInfo> addedPrinters = null;
|
||||||
|
List<PrinterInfo> updatedPrinters = null;
|
||||||
|
List<PrinterId> removedPrinterIds = null;
|
||||||
|
|
||||||
|
// Determine the added and updated printers.
|
||||||
|
for (PrinterInfo printer : mPrinters.values()) {
|
||||||
|
PrinterInfo sentPrinter = mLastSentPrinters.get(printer.getId());
|
||||||
|
if (sentPrinter != null) {
|
||||||
|
if (!sentPrinter.equals(printer)) {
|
||||||
|
if (updatedPrinters == null) {
|
||||||
|
updatedPrinters = new ArrayList<PrinterInfo>();
|
||||||
|
}
|
||||||
|
updatedPrinters.add(printer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (addedPrinters == null) {
|
||||||
|
addedPrinters = new ArrayList<PrinterInfo>();
|
||||||
|
}
|
||||||
|
addedPrinters.add(printer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the added printers, if such.
|
||||||
|
if (addedPrinters != null) {
|
||||||
|
sendAddedPrinters(mObserver, addedPrinters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the updated printers, if such.
|
||||||
|
if (updatedPrinters != null) {
|
||||||
|
sendUpdatedPrinters(mObserver, updatedPrinters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the removed printers.
|
||||||
|
for (PrinterInfo sentPrinter : mLastSentPrinters.values()) {
|
||||||
|
if (!mPrinters.containsKey(sentPrinter.getId())) {
|
||||||
|
if (removedPrinterIds == null) {
|
||||||
|
removedPrinterIds = new ArrayList<PrinterId>();
|
||||||
|
}
|
||||||
|
removedPrinterIds.add(sentPrinter.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the removed printers, if such.
|
||||||
|
if (removedPrinterIds != null) {
|
||||||
|
sendRemovedPrinters(mObserver, removedPrinterIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastSentPrinters = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback notifying you that the session is open and you should start
|
* Callback asking you to start printer discovery. Discovered printers should be
|
||||||
* printer discovery. Discovered printers should be added via calling
|
* added via calling {@link #addPrinters(List)}. Added printers that disappeared
|
||||||
* {@link #addPrinters(List)}. Added printers that disappeared should be
|
* should be removed via calling {@link #removePrinters(List)}. Added printers
|
||||||
* removed via calling {@link #removePrinters(List)}. Added printers whose
|
* whose properties or capabilities changed should be updated via calling {@link
|
||||||
* properties or capabilities changes should be updated via calling {@link
|
* #updatePrinters(List)}. You will receive a call to call to {@link
|
||||||
* #updatePrinters(List)}. When the session is closed you will receive a
|
* #onStopPrinterDiscovery()} when you should stop printer discovery.
|
||||||
* call to {@link #onClose()}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* During printer discovery all printers that are known to your print
|
* During the lifetime of this session all printers that are known to your print
|
||||||
* service have to be added. The system does not retain any printers from
|
* service have to be added. The system does not retain any printers across sessions.
|
||||||
* previous sessions.
|
* However, if you were asked to start and then stop performing printer discovery
|
||||||
|
* in this session, then a subsequent discovering should not re-discover already
|
||||||
|
* discovered printers.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Note: </strong>You are also given a list of printers whose
|
* <strong>Note: </strong>You are also given a list of printers whose availability
|
||||||
* availability has to be checked first. For example, these printers could
|
* has to be checked first. For example, these printers could be the user's favorite
|
||||||
* be the user's favorite ones, therefore they have to be verified first.
|
* ones, therefore they have to be verified first.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @see #onClose()
|
* @param priorityList The list of printers to validate first. Never null.
|
||||||
|
*
|
||||||
|
* @see #onStopPrinterDiscovery()
|
||||||
* @see #addPrinters(List)
|
* @see #addPrinters(List)
|
||||||
* @see #removePrinters(List)
|
* @see #removePrinters(List)
|
||||||
* @see #updatePrinters(List)
|
* @see #updatePrinters(List)
|
||||||
|
* @see #isPrinterDiscoveryStarted()
|
||||||
*/
|
*/
|
||||||
public abstract void onOpen(List<PrinterId> priorityList);
|
public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback notifying you that the session is closed and you should stop
|
* Callback notifying you that you should stop printer discovery.
|
||||||
* printer discovery. After the session is closed any call to the methods
|
*
|
||||||
* of this instance will be ignored. Once the session is closed
|
* @see #onStartPrinterDiscovery(List)
|
||||||
* it will never be opened again.
|
* @see #isPrinterDiscoveryStarted()
|
||||||
*/
|
*/
|
||||||
public abstract void onClose();
|
public abstract void onStopPrinterDiscovery();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that you update a printer. You are responsible for updating
|
* Requests that you update a printer. You are responsible for updating
|
||||||
@@ -255,77 +486,72 @@ public abstract class PrinterDiscoverySession {
|
|||||||
*/
|
*/
|
||||||
public abstract void onRequestPrinterUpdate(PrinterId printerId);
|
public abstract void onRequestPrinterUpdate(PrinterId printerId);
|
||||||
|
|
||||||
void close() {
|
/**
|
||||||
synchronized (mLock) {
|
* Notifies you that the session is destroyed. After this callback is invoked
|
||||||
mController = null;
|
* any calls to the methods of this class will be ignored, {@link #isDestroyed()}
|
||||||
|
* will return true and you will also no longer receive callbacks.
|
||||||
|
*
|
||||||
|
* @see #isDestroyed()
|
||||||
|
*/
|
||||||
|
public abstract void onDestroy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the session is destroyed.
|
||||||
|
*
|
||||||
|
* @return Whether the session is destroyed.
|
||||||
|
*
|
||||||
|
* @see #onDestroy()
|
||||||
|
*/
|
||||||
|
public final boolean isDestroyed() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
|
return mIsDestroyed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether printer discovery is started.
|
||||||
|
*
|
||||||
|
* @return Whether printer discovery is destroyed.
|
||||||
|
*
|
||||||
|
* @see #onStartPrinterDiscovery(List)
|
||||||
|
* @see #onStopPrinterDiscovery()
|
||||||
|
*/
|
||||||
|
public final boolean isPrinterDiscoveryStarted() {
|
||||||
|
PrintService.throwIfNotCalledOnMainThread();
|
||||||
|
return mIsDiscoveryStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startPrinterDiscovery(List<PrinterId> priorityList) {
|
||||||
|
if (!mIsDestroyed) {
|
||||||
|
mIsDiscoveryStarted = true;
|
||||||
|
sendOutOfDiscoveryPeriodPrinterChanges();
|
||||||
|
if (priorityList == null) {
|
||||||
|
priorityList = Collections.emptyList();
|
||||||
|
}
|
||||||
|
onStartPrinterDiscovery(priorityList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopPrinterDiscovery() {
|
||||||
|
if (!mIsDestroyed) {
|
||||||
|
mIsDiscoveryStarted = false;
|
||||||
|
onStopPrinterDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void requestPrinterUpdate(PrinterId printerId) {
|
||||||
|
if (!mIsDestroyed) {
|
||||||
|
onRequestPrinterUpdate(printerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy() {
|
||||||
|
if (!mIsDestroyed) {
|
||||||
|
mIsDestroyed = true;
|
||||||
|
mIsDiscoveryStarted = false;
|
||||||
|
mPrinters.clear();
|
||||||
|
mLastSentPrinters = null;
|
||||||
mObserver = null;
|
mObserver = null;
|
||||||
|
onDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class SessionHandler extends Handler {
|
|
||||||
public static final int MSG_OPEN = 1;
|
|
||||||
public static final int MSG_CLOSE = 2;
|
|
||||||
public static final int MSG_REQUEST_PRINTER_UPDATE = 3;
|
|
||||||
|
|
||||||
public SessionHandler(Looper looper) {
|
|
||||||
super(looper, null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
switch (message.what) {
|
|
||||||
case MSG_OPEN: {
|
|
||||||
List<PrinterId> priorityList = (List<PrinterId>) message.obj;
|
|
||||||
onOpen(priorityList);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MSG_CLOSE: {
|
|
||||||
onClose();
|
|
||||||
close();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MSG_REQUEST_PRINTER_UPDATE: {
|
|
||||||
PrinterId printerId = (PrinterId) message.obj;
|
|
||||||
onRequestPrinterUpdate(printerId);
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class PrinterDiscoverySessionController extends
|
|
||||||
IPrinterDiscoverySessionController.Stub {
|
|
||||||
private final WeakReference<PrinterDiscoverySession> mWeakSession;
|
|
||||||
|
|
||||||
public PrinterDiscoverySessionController(PrinterDiscoverySession session) {
|
|
||||||
mWeakSession = new WeakReference<PrinterDiscoverySession>(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(List<PrinterId> priorityList) {
|
|
||||||
PrinterDiscoverySession session = mWeakSession.get();
|
|
||||||
if (session != null) {
|
|
||||||
session.mHandler.obtainMessage(SessionHandler.MSG_OPEN,
|
|
||||||
priorityList).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
PrinterDiscoverySession session = mWeakSession.get();
|
|
||||||
if (session != null) {
|
|
||||||
session.mHandler.sendEmptyMessage(SessionHandler.MSG_CLOSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestPrinterUpdate(PrinterId printerId) {
|
|
||||||
PrinterDiscoverySession session = mWeakSession.get();
|
|
||||||
if (session != null) {
|
|
||||||
session.mHandler.obtainMessage(
|
|
||||||
SessionHandler.MSG_REQUEST_PRINTER_UPDATE,
|
|
||||||
printerId).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ public class HandlerCaller {
|
|||||||
mH.sendMessage(msg);
|
mH.sendMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendMessageDelayed(Message msg, long delayMillis) {
|
||||||
|
mH.sendMessageDelayed(msg, delayMillis);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasMessages(int what) {
|
public boolean hasMessages(int what) {
|
||||||
return mH.hasMessages(what);
|
return mH.hasMessages(what);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.android.printspooler"
|
package="com.android.printspooler"
|
||||||
android:sharedUserId="android.uid.printspooler"
|
android:sharedUserId="android.uid.system"
|
||||||
android:versionName="1"
|
android:versionName="1"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
coreApp="true">
|
coreApp="true">
|
||||||
@@ -51,9 +51,10 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ChoosePrinterActivity"
|
android:name=".SelectPrinterActivity"
|
||||||
android:exported="false"
|
android:label="@string/all_printers_label"
|
||||||
android:theme="@android:style/Theme.Holo.Light">
|
android:theme="@style/SelectPrinterActivityTheme"
|
||||||
|
android:exported="false">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
|
|||||||
BIN
packages/PrintSpooler/res/drawable-hdpi/ic_menu_add.png
Normal file
BIN
packages/PrintSpooler/res/drawable-hdpi/ic_menu_add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 667 B |
BIN
packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png
Normal file
BIN
packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 596 B |
BIN
packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png
Normal file
BIN
packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 761 B |
@@ -20,9 +20,4 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="@color/container_background">
|
android:background="@color/container_background">
|
||||||
|
|
||||||
<include
|
|
||||||
layout="@layout/print_job_config_activity_content_editing">
|
|
||||||
</include>
|
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -14,10 +14,16 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/list_view"
|
android:orientation="horizontal"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical">
|
|
||||||
</ListView>
|
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:name="com.android.printspooler.SelectPrinterFragment"
|
||||||
|
android:id="@+id/select_printer_fragment"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="8dip"
|
android:paddingStart="8dip"
|
||||||
android:paddingEnd="8dip"
|
android:paddingEnd="8dip"
|
||||||
|
|||||||
@@ -23,7 +23,15 @@
|
|||||||
android:actionViewClass="android.widget.SearchView"
|
android:actionViewClass="android.widget.SearchView"
|
||||||
android:showAsAction="ifRoom"
|
android:showAsAction="ifRoom"
|
||||||
android:alphabeticShortcut="f"
|
android:alphabeticShortcut="f"
|
||||||
android:imeOptions="actionSearch">
|
android:imeOptions="actionSearch">
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_printer"
|
||||||
|
android:title="@null"
|
||||||
|
android:icon="@drawable/ic_menu_add"
|
||||||
|
android:showAsAction="ifRoom"
|
||||||
|
android:alphabeticShortcut="a">
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
@@ -58,11 +58,32 @@
|
|||||||
<!-- Title for the temporary dialog show while an app is generating a print job. [CHAR LIMIT=30] -->
|
<!-- Title for the temporary dialog show while an app is generating a print job. [CHAR LIMIT=30] -->
|
||||||
<string name="generating_print_job">Generating print job</string>
|
<string name="generating_print_job">Generating print job</string>
|
||||||
|
|
||||||
<!-- Choose printer activity -->
|
<!-- Title for the save as PDF option in the printer list. [CHAR LIMIT=30] -->
|
||||||
|
<string name="save_as_pdf">Save as PDF</string>
|
||||||
|
|
||||||
|
<!-- Title for the open all printers UI option in the printer list. [CHAR LIMIT=30] -->
|
||||||
|
<string name="all_printers">All printers\.\.\.</string>
|
||||||
|
|
||||||
|
<!-- Title for the searching for printers option in the printer list
|
||||||
|
(only option if not printers are available). [CHAR LIMIT=40] -->
|
||||||
|
<string name="searching_for_printers">Searching for printers\.\.\.</string>
|
||||||
|
|
||||||
|
<!-- Select printer activity -->
|
||||||
|
|
||||||
<!-- Title for the share action bar menu item. [CHAR LIMIT=20] -->
|
<!-- Title for the share action bar menu item. [CHAR LIMIT=20] -->
|
||||||
<string name="search">Search</string>
|
<string name="search">Search</string>
|
||||||
|
|
||||||
|
<!-- Title for the select printer activity. [CHAR LIMIT=30] -->
|
||||||
|
<string name="all_printers_label">All printers</string>
|
||||||
|
|
||||||
|
<!-- Add printer dialog -->
|
||||||
|
|
||||||
|
<!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
|
||||||
|
<string name="choose_print_service">Choose print service</string>
|
||||||
|
|
||||||
|
<!-- Title for the button to search the play store for print services. [CHAR LIMIT=50] -->
|
||||||
|
<string name="search_play_store">Search in play store</string>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
|
|
||||||
<!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
|
<!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
|
||||||
|
|||||||
@@ -24,4 +24,12 @@
|
|||||||
<item name="android:colorBackgroundCacheHint">@android:color/transparent</item>
|
<item name="android:colorBackgroundCacheHint">@android:color/transparent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SelectPrinterActivityTheme" parent="@android:style/Theme.Holo.Light">
|
||||||
|
<item name="android:actionBarStyle">@style/SelectPrinterActivityActionBarStyle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="SelectPrinterActivityActionBarStyle" parent="@android:style/Widget.Holo.ActionBar">
|
||||||
|
<item name="android:displayOptions">showTitle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,285 +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 com.android.printspooler;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.print.IPrinterDiscoverySessionController;
|
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrinterId;
|
|
||||||
import android.print.PrinterInfo;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is responsible to provide the available printers.
|
|
||||||
* It starts and stops printer discovery and manages the returned
|
|
||||||
* printers.
|
|
||||||
*/
|
|
||||||
public class AvailablePrinterProvider extends DataProvider<PrinterInfo>
|
|
||||||
implements DataLoader {
|
|
||||||
private static final String LOG_TAG = "AvailablePrinterProvider";
|
|
||||||
|
|
||||||
private final Set<PrinterId> mPrinteIdsSet = new ArraySet<PrinterId>();
|
|
||||||
|
|
||||||
private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
|
|
||||||
|
|
||||||
private final List<PrinterId> mPriorityList;
|
|
||||||
|
|
||||||
private PrinterDiscoverySession mDiscoverySession;
|
|
||||||
|
|
||||||
public AvailablePrinterProvider(Context context, List<PrinterId> priorityList) {
|
|
||||||
mDiscoverySession = new PrinterDiscoverySession(context.getMainLooper());
|
|
||||||
mPriorityList = priorityList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startLoadData() {
|
|
||||||
mDiscoverySession.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopLoadData() {
|
|
||||||
mDiscoverySession.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mPrinters.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemIndex(PrinterInfo printer) {
|
|
||||||
return mPrinters.indexOf(printer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PrinterInfo getItemAt(int index) {
|
|
||||||
return mPrinters.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refreshItem(int index) {
|
|
||||||
PrinterInfo printer = getItemAt(index);
|
|
||||||
mDiscoverySession.requestPrinterUpdate(printer.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPrinters(List<PrinterInfo> printers) {
|
|
||||||
boolean addedPrinters = false;
|
|
||||||
|
|
||||||
final int addedPrinterCount = printers.size();
|
|
||||||
for (int i = 0; i < addedPrinterCount; i++) {
|
|
||||||
PrinterInfo addedPrinter = printers.get(i);
|
|
||||||
if (mPrinteIdsSet.add(addedPrinter.getId())) {
|
|
||||||
mPrinters.add(addedPrinter);
|
|
||||||
addedPrinters = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addedPrinters) {
|
|
||||||
notifyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updatePrinters(List<PrinterInfo> printers) {
|
|
||||||
boolean updatedPrinters = false;
|
|
||||||
|
|
||||||
final int updatedPrinterCount = printers.size();
|
|
||||||
for (int i = 0; i < updatedPrinterCount; i++) {
|
|
||||||
PrinterInfo updatedPrinter = printers.get(i);
|
|
||||||
if (mPrinteIdsSet.contains(updatedPrinter.getId())) {
|
|
||||||
final int oldPrinterCount = mPrinters.size();
|
|
||||||
for (int j = 0; j < oldPrinterCount; j++) {
|
|
||||||
PrinterInfo oldPrinter = mPrinters.get(j);
|
|
||||||
if (updatedPrinter.getId().equals(oldPrinter.getId())) {
|
|
||||||
mPrinters.set(j, updatedPrinter);
|
|
||||||
updatedPrinters = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatedPrinters) {
|
|
||||||
notifyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removePrinters(List<PrinterId> printers) {
|
|
||||||
boolean removedPrinters = false;
|
|
||||||
|
|
||||||
final int removedPrinterCount = printers.size();
|
|
||||||
for (int i = 0; i < removedPrinterCount; i++) {
|
|
||||||
PrinterId removedPrinter = printers.get(i);
|
|
||||||
if (mPrinteIdsSet.contains(removedPrinter)) {
|
|
||||||
mPrinteIdsSet.remove(removedPrinter);
|
|
||||||
Iterator<PrinterInfo> iterator = mPrinters.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
PrinterInfo oldPrinter = iterator.next();
|
|
||||||
if (removedPrinter.equals(oldPrinter.getId())) {
|
|
||||||
iterator.remove();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removedPrinters) {
|
|
||||||
notifyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class PrinterDiscoverySession {
|
|
||||||
|
|
||||||
private final Handler mHandler;
|
|
||||||
|
|
||||||
private final IPrinterDiscoverySessionObserver mObserver;
|
|
||||||
|
|
||||||
private IPrinterDiscoverySessionController mController;
|
|
||||||
|
|
||||||
public PrinterDiscoverySession(Looper looper) {
|
|
||||||
mHandler = new SessionHandler(looper);
|
|
||||||
mObserver = new PrinterDiscoverySessionObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void open() {
|
|
||||||
PrintSpooler.peekInstance().createPrinterDiscoverySession(
|
|
||||||
mObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
if (mController != null) {
|
|
||||||
try {
|
|
||||||
mController.close();
|
|
||||||
} catch (RemoteException re) {
|
|
||||||
Log.e(LOG_TAG, "Error closing printer discovery session", re);
|
|
||||||
} finally {
|
|
||||||
mController = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestPrinterUpdate(PrinterId printerId) {
|
|
||||||
if (mController != null) {
|
|
||||||
try {
|
|
||||||
mController.requestPrinterUpdate(printerId);
|
|
||||||
} catch (RemoteException re) {
|
|
||||||
Log.e(LOG_TAG, "Error requesting printer udpdate", re);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class SessionHandler extends Handler {
|
|
||||||
public static final int MSG_SET_CONTROLLER = 1;
|
|
||||||
public static final int MSG_ON_PRINTERS_ADDED = 2;
|
|
||||||
public static final int MSG_ON_PRINTERS_REMOVED = 3;
|
|
||||||
public static final int MSG_ON_PRINTERS_UPDATED = 4;
|
|
||||||
|
|
||||||
public SessionHandler(Looper looper) {
|
|
||||||
super(looper, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
switch (message.what) {
|
|
||||||
case MSG_SET_CONTROLLER: {
|
|
||||||
mController = (IPrinterDiscoverySessionController) message.obj;
|
|
||||||
try {
|
|
||||||
mController.open(mPriorityList);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(LOG_TAG, "Error starting printer discovery");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MSG_ON_PRINTERS_ADDED: {
|
|
||||||
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
|
|
||||||
addPrinters(printers);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MSG_ON_PRINTERS_REMOVED: {
|
|
||||||
List<PrinterId> printers = (List<PrinterId>) message.obj;
|
|
||||||
removePrinters(printers);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MSG_ON_PRINTERS_UPDATED: {
|
|
||||||
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
|
|
||||||
updatePrinters(printers);
|
|
||||||
} break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class PrinterDiscoverySessionObserver
|
|
||||||
extends IPrinterDiscoverySessionObserver.Stub {
|
|
||||||
|
|
||||||
private final WeakReference<PrinterDiscoverySession> mWeakSession;
|
|
||||||
|
|
||||||
public PrinterDiscoverySessionObserver(PrinterDiscoverySession session) {
|
|
||||||
mWeakSession = new WeakReference<PrinterDiscoverySession>(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setController(IPrinterDiscoverySessionController controller) {
|
|
||||||
PrinterDiscoverySession sesison = mWeakSession.get();
|
|
||||||
if (sesison != null) {
|
|
||||||
sesison.mHandler.obtainMessage(
|
|
||||||
PrinterDiscoverySession.SessionHandler.MSG_SET_CONTROLLER,
|
|
||||||
controller).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrintersAdded(List<PrinterInfo> printers) {
|
|
||||||
PrinterDiscoverySession sesison = mWeakSession.get();
|
|
||||||
if (sesison != null) {
|
|
||||||
sesison.mHandler.obtainMessage(
|
|
||||||
PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_ADDED,
|
|
||||||
printers).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrintersRemoved(List<PrinterId> printers) {
|
|
||||||
PrinterDiscoverySession session = mWeakSession.get();
|
|
||||||
if (session != null) {
|
|
||||||
session.mHandler.obtainMessage(
|
|
||||||
PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_REMOVED,
|
|
||||||
printers).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrintersUpdated(List<PrinterInfo> printers) {
|
|
||||||
PrinterDiscoverySession session = mWeakSession.get();
|
|
||||||
if (session != null) {
|
|
||||||
session.mHandler.obtainMessage(
|
|
||||||
PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_UPDATED,
|
|
||||||
printers).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,33 +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 com.android.printspooler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the contract for a class that know how to load data.
|
|
||||||
*/
|
|
||||||
public interface DataLoader {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests to start loading data.
|
|
||||||
*/
|
|
||||||
public void startLoadData();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests to stop loading data.
|
|
||||||
*/
|
|
||||||
public void stopLoadData();
|
|
||||||
}
|
|
||||||
@@ -1,50 +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 com.android.printspooler;
|
|
||||||
|
|
||||||
import android.database.DataSetObservable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the simple contract for data providers.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the providers data.
|
|
||||||
*/
|
|
||||||
public abstract class DataProvider<T> extends DataSetObservable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of items.
|
|
||||||
*
|
|
||||||
* @return The item count.
|
|
||||||
*/
|
|
||||||
public abstract int getItemCount();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the index of an item.
|
|
||||||
*
|
|
||||||
* @param item The item.
|
|
||||||
* @return The item index.
|
|
||||||
*/
|
|
||||||
public abstract int getItemIndex(T item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an item at a given position.
|
|
||||||
*
|
|
||||||
* @param index The position.
|
|
||||||
* @return The item.
|
|
||||||
*/
|
|
||||||
public abstract T getItemAt(int index);
|
|
||||||
}
|
|
||||||
@@ -1,364 +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 com.android.printspooler;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.print.PrinterId;
|
|
||||||
import android.print.PrinterInfo;
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.AtomicFile;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Slog;
|
|
||||||
import android.util.Xml;
|
|
||||||
|
|
||||||
import com.android.internal.util.FastXmlSerializer;
|
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides the favorite printers based on past usage.
|
|
||||||
*/
|
|
||||||
final class FavoritePrinterProvider extends DataProvider<PrinterInfo> implements DataLoader {
|
|
||||||
|
|
||||||
private static final String LOG_TAG = "FavoritePrinterProvider";
|
|
||||||
|
|
||||||
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
|
|
||||||
|
|
||||||
private static final int MAX_HISTORY_LENGTH = 50;
|
|
||||||
|
|
||||||
private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
|
|
||||||
|
|
||||||
private final List<PrinterRecord> mHistoricalPrinters = new ArrayList<PrinterRecord>();
|
|
||||||
|
|
||||||
private final List<PrinterRecord> mFavoritePrinters = new ArrayList<PrinterRecord>();
|
|
||||||
|
|
||||||
private final PersistenceManager mPersistenceManager;
|
|
||||||
|
|
||||||
public FavoritePrinterProvider(Context context) {
|
|
||||||
mPersistenceManager = new PersistenceManager(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPrinter(PrinterInfo printer) {
|
|
||||||
addPrinterInternal(printer);
|
|
||||||
computeFavoritePrinters();
|
|
||||||
mPersistenceManager.writeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mFavoritePrinters.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PrinterInfo getItemAt(int index) {
|
|
||||||
return mFavoritePrinters.get(index).printer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemIndex(PrinterInfo printer) {
|
|
||||||
return mFavoritePrinters.indexOf(printer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startLoadData() {
|
|
||||||
mPersistenceManager.readStateLocked();
|
|
||||||
computeFavoritePrinters();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopLoadData() {
|
|
||||||
/* do nothing */
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPrinterInternal(PrinterInfo printer) {
|
|
||||||
if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
|
|
||||||
mHistoricalPrinters.remove(0);
|
|
||||||
}
|
|
||||||
mHistoricalPrinters.add(new PrinterRecord(printer));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void computeFavoritePrinters() {
|
|
||||||
Map<PrinterId, PrinterRecord> recordMap =
|
|
||||||
new ArrayMap<PrinterId, PrinterRecord>();
|
|
||||||
|
|
||||||
// Recompute the weights.
|
|
||||||
float currentWeight = 1.0f;
|
|
||||||
final int printerCount = mHistoricalPrinters.size();
|
|
||||||
for (int i = printerCount - 1; i >= 0; i--) {
|
|
||||||
PrinterRecord record = mHistoricalPrinters.get(i);
|
|
||||||
record.weight = currentWeight;
|
|
||||||
// Aggregate weight for the same printer
|
|
||||||
PrinterRecord oldRecord = recordMap.put(record.printer.getId(), record);
|
|
||||||
if (oldRecord != null) {
|
|
||||||
record.weight += oldRecord.weight;
|
|
||||||
}
|
|
||||||
currentWeight *= WEIGHT_DECAY_COEFFICIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the unique printer records with computed weights.
|
|
||||||
mFavoritePrinters.addAll(recordMap.values());
|
|
||||||
|
|
||||||
// Soft the favorite printers.
|
|
||||||
Collections.sort(mFavoritePrinters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class PrinterRecord implements Comparable<PrinterRecord> {
|
|
||||||
public final PrinterInfo printer;
|
|
||||||
public float weight;
|
|
||||||
|
|
||||||
public PrinterRecord(PrinterInfo printer) {
|
|
||||||
this.printer = printer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(PrinterRecord another) {
|
|
||||||
return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class PersistenceManager {
|
|
||||||
private static final String PERSIST_FILE_NAME = "printer_history.xml";
|
|
||||||
|
|
||||||
private static final String TAG_PRINTERS = "printers";
|
|
||||||
|
|
||||||
private static final String TAG_PRINTER = "printer";
|
|
||||||
private static final String TAG_PRINTER_ID = "printerId";
|
|
||||||
|
|
||||||
private static final String ATTR_LOCAL_ID = "localId";
|
|
||||||
private static final String ATTR_SERVICE_NAME = "serviceName";
|
|
||||||
|
|
||||||
private static final String ATTR_NAME = "name";
|
|
||||||
private static final String ATTR_DESCRIPTION = "description";
|
|
||||||
private static final String ATTR_STATUS = "status";
|
|
||||||
|
|
||||||
private final AtomicFile mStatePersistFile;
|
|
||||||
|
|
||||||
private PersistenceManager(Context context) {
|
|
||||||
mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
|
|
||||||
PERSIST_FILE_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void writeState() {
|
|
||||||
|
|
||||||
new AsyncTask<List<PrinterRecord>, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(List<PrinterRecord>... printers) {
|
|
||||||
doWriteState(printers[0]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void result) {
|
|
||||||
notifyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
|
|
||||||
new ArrayList<PrinterRecord>(mHistoricalPrinters));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doWriteState(List<PrinterRecord> printers) {
|
|
||||||
FileOutputStream out = null;
|
|
||||||
try {
|
|
||||||
out = mStatePersistFile.startWrite();
|
|
||||||
|
|
||||||
XmlSerializer serializer = new FastXmlSerializer();
|
|
||||||
serializer.setOutput(out, "utf-8");
|
|
||||||
serializer.startDocument(null, true);
|
|
||||||
serializer.startTag(null, TAG_PRINTERS);
|
|
||||||
|
|
||||||
final int printerCount = printers.size();
|
|
||||||
for (int i = printerCount - 1; i >= 0; i--) {
|
|
||||||
PrinterInfo printer = printers.get(i).printer;
|
|
||||||
|
|
||||||
serializer.startTag(null, TAG_PRINTER);
|
|
||||||
|
|
||||||
serializer.attribute(null, ATTR_NAME, printer.getName());
|
|
||||||
serializer.attribute(null, ATTR_STATUS, String.valueOf(printer.getStatus()));
|
|
||||||
String description = printer.getDescription();
|
|
||||||
if (description != null) {
|
|
||||||
serializer.attribute(null, ATTR_DESCRIPTION, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrinterId printerId = printer.getId();
|
|
||||||
serializer.startTag(null, TAG_PRINTER_ID);
|
|
||||||
serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
|
|
||||||
serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
|
|
||||||
.flattenToString());
|
|
||||||
serializer.endTag(null, TAG_PRINTER_ID);
|
|
||||||
|
|
||||||
serializer.endTag(null, TAG_PRINTER);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.i(LOG_TAG, "[PERSISTED] " + printer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer.endTag(null, TAG_PRINTERS);
|
|
||||||
serializer.endDocument();
|
|
||||||
mStatePersistFile.finishWrite(out);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.i(LOG_TAG, "[PERSIST END]");
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed to write printer history, restoring backup.", ioe);
|
|
||||||
mStatePersistFile.failWrite(out);
|
|
||||||
} finally {
|
|
||||||
IoUtils.closeQuietly(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readStateLocked() {
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
|
||||||
in = mStatePersistFile.openRead();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.i(LOG_TAG, "No existing printer history.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
|
||||||
parser.setInput(in, null);
|
|
||||||
parseState(parser);
|
|
||||||
} catch (IllegalStateException ise) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", ise);
|
|
||||||
} catch (NullPointerException npe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", npe);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", nfe);
|
|
||||||
} catch (XmlPullParserException xppe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", xppe);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", ioe);
|
|
||||||
} catch (IndexOutOfBoundsException iobe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", iobe);
|
|
||||||
} finally {
|
|
||||||
IoUtils.closeQuietly(in);
|
|
||||||
}
|
|
||||||
notifyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseState(XmlPullParser parser)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.START_TAG, TAG_PRINTERS);
|
|
||||||
parser.next();
|
|
||||||
|
|
||||||
while (parsePrinter(parser)) {
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_PRINTERS);
|
|
||||||
|
|
||||||
// We were reading the new records first and appended them first,
|
|
||||||
// hence the historical list is in a reversed order, so fix that.
|
|
||||||
Collections.reverse(mHistoricalPrinters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean parsePrinter(XmlPullParser parser)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (!accept(parser, XmlPullParser.START_TAG, TAG_PRINTER)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
|
||||||
String description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
|
|
||||||
final int status = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATUS));
|
|
||||||
|
|
||||||
parser.next();
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID);
|
|
||||||
String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
|
|
||||||
ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
|
|
||||||
null, ATTR_SERVICE_NAME));
|
|
||||||
PrinterId printerId = new PrinterId(service, localId);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
|
|
||||||
parser.next();
|
|
||||||
|
|
||||||
PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, name, status);
|
|
||||||
builder.setDescription(description);
|
|
||||||
PrinterInfo printer = builder.create();
|
|
||||||
|
|
||||||
addPrinterInternal(printer);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.i(LOG_TAG, "[RESTORED] " + printer);
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expect(XmlPullParser parser, int type, String tag)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
if (!accept(parser, type, tag)) {
|
|
||||||
throw new XmlPullParserException("Exepected event: " + type
|
|
||||||
+ " and tag: " + tag + " but got event: " + parser.getEventType()
|
|
||||||
+ " and tag:" + parser.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void skipEmptyTextTags(XmlPullParser parser)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
while (accept(parser, XmlPullParser.TEXT, null)
|
|
||||||
&& "\n".equals(parser.getText())) {
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean accept(XmlPullParser parser, int type, String tag)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
if (parser.getEventType() != type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tag != null) {
|
|
||||||
if (!tag.equals(parser.getName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (parser.getName() != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,575 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.android.printspooler;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Loader;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.print.PrinterId;
|
||||||
|
import android.print.PrinterInfo;
|
||||||
|
import android.util.ArrayMap;
|
||||||
|
import android.util.AtomicFile;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Slog;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.android.internal.util.FastXmlSerializer;
|
||||||
|
import com.android.printspooler.PrintSpoolerService.PrinterDiscoverySession;
|
||||||
|
|
||||||
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for loading printers by doing discovery
|
||||||
|
* and merging the discovered printers with the previously used ones.
|
||||||
|
*/
|
||||||
|
public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
|
||||||
|
private static final String LOG_TAG = "FusedPrintersProvider";
|
||||||
|
|
||||||
|
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
|
||||||
|
|
||||||
|
private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
|
||||||
|
|
||||||
|
private static final int MAX_HISTORY_LENGTH = 50;
|
||||||
|
|
||||||
|
private static final int MAX_HISTORICAL_PRINTER_COUNT = 4;
|
||||||
|
|
||||||
|
private final Map<PrinterId, PrinterInfo> mPrinters =
|
||||||
|
new LinkedHashMap<PrinterId, PrinterInfo>();
|
||||||
|
|
||||||
|
private final PersistenceManager mPersistenceManager;
|
||||||
|
|
||||||
|
private PrinterDiscoverySession mDiscoverySession;
|
||||||
|
|
||||||
|
private List<PrinterInfo> mFavoritePrinters;
|
||||||
|
|
||||||
|
public FusedPrintersProvider(Context context) {
|
||||||
|
super(context);
|
||||||
|
mPersistenceManager = new PersistenceManager(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHistoricalPrinter(PrinterInfo printer) {
|
||||||
|
mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PrinterInfo> getPrinters() {
|
||||||
|
return new ArrayList<PrinterInfo>(mPrinters.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deliverResult(List<PrinterInfo> printers) {
|
||||||
|
if (isStarted()) {
|
||||||
|
super.deliverResult(printers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStartLoading() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onStartLoading()");
|
||||||
|
}
|
||||||
|
// The contract is that if we already have a valid,
|
||||||
|
// result the we have to deliver it immediately.
|
||||||
|
if (!mPrinters.isEmpty()) {
|
||||||
|
deliverResult(new ArrayList<PrinterInfo>(mPrinters.values()));
|
||||||
|
}
|
||||||
|
// If the data has changed since the last load
|
||||||
|
// or is not available, start a load.
|
||||||
|
if (takeContentChanged() || mPrinters.isEmpty()) {
|
||||||
|
onForceLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStopLoading() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onStopLoading()");
|
||||||
|
}
|
||||||
|
onCancelLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onForceLoad() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onForceLoad()");
|
||||||
|
}
|
||||||
|
onCancelLoad();
|
||||||
|
loadInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadInternal() {
|
||||||
|
if (mDiscoverySession == null) {
|
||||||
|
mDiscoverySession = new MyPrinterDiscoverySession();
|
||||||
|
mPersistenceManager.readPrinterHistory();
|
||||||
|
}
|
||||||
|
if (mPersistenceManager.isReadHistoryCompleted()
|
||||||
|
&& !mDiscoverySession.isStarted()) {
|
||||||
|
final int favoriteCount = Math.min(MAX_HISTORICAL_PRINTER_COUNT,
|
||||||
|
mFavoritePrinters.size());
|
||||||
|
List<PrinterId> printerIds = new ArrayList<PrinterId>(favoriteCount);
|
||||||
|
for (int i = 0; i < favoriteCount; i++) {
|
||||||
|
printerIds.add(mFavoritePrinters.get(i).getId());
|
||||||
|
}
|
||||||
|
mDiscoverySession.startPrinterDisovery(printerIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onCancelLoad() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onCancelLoad()");
|
||||||
|
}
|
||||||
|
return cancelInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cancelInternal() {
|
||||||
|
if (mDiscoverySession != null && mDiscoverySession.isStarted()) {
|
||||||
|
mDiscoverySession.stopPrinterDiscovery();
|
||||||
|
return true;
|
||||||
|
} else if (mPersistenceManager.isReadHistoryInProgress()) {
|
||||||
|
return mPersistenceManager.stopReadPrinterHistory();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onReset() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onReset()");
|
||||||
|
}
|
||||||
|
onStopLoading();
|
||||||
|
mPrinters.clear();
|
||||||
|
if (mDiscoverySession != null) {
|
||||||
|
mDiscoverySession.destroy();
|
||||||
|
mDiscoverySession = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAbandon() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onAbandon()");
|
||||||
|
}
|
||||||
|
onStopLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshPrinter(PrinterId printerId) {
|
||||||
|
if (isStarted() && mDiscoverySession != null && mDiscoverySession.isStarted()) {
|
||||||
|
mDiscoverySession.requestPrinterUpdated(printerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class MyPrinterDiscoverySession extends PrinterDiscoverySession {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrintersAdded(List<PrinterInfo> printers) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "MyPrinterDiscoverySession#onPrintersAdded()");
|
||||||
|
}
|
||||||
|
boolean printersAdded = false;
|
||||||
|
final int addedPrinterCount = printers.size();
|
||||||
|
for (int i = 0; i < addedPrinterCount; i++) {
|
||||||
|
PrinterInfo printer = printers.get(i);
|
||||||
|
if (!mPrinters.containsKey(printer.getId())) {
|
||||||
|
mPrinters.put(printer.getId(), printer);
|
||||||
|
printersAdded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (printersAdded) {
|
||||||
|
deliverResult(new ArrayList<PrinterInfo>(mPrinters.values()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrintersRemoved(List<PrinterId> printerIds) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "MyPrinterDiscoverySession#onPrintersRemoved()");
|
||||||
|
}
|
||||||
|
boolean removedPrinters = false;
|
||||||
|
final int removedPrinterCount = printerIds.size();
|
||||||
|
for (int i = 0; i < removedPrinterCount; i++) {
|
||||||
|
PrinterId removedPrinterId = printerIds.get(i);
|
||||||
|
if (mPrinters.remove(removedPrinterId) != null) {
|
||||||
|
removedPrinters = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removedPrinters) {
|
||||||
|
deliverResult(new ArrayList<PrinterInfo>(mPrinters.values()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrintersUpdated(List<PrinterInfo> printers) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "MyPrinterDiscoverySession#onPrintersUpdated()");
|
||||||
|
}
|
||||||
|
boolean updatedPrinters = false;
|
||||||
|
final int updatedPrinterCount = printers.size();
|
||||||
|
for (int i = 0; i < updatedPrinterCount; i++) {
|
||||||
|
PrinterInfo updatedPrinter = printers.get(i);
|
||||||
|
if (mPrinters.containsKey(updatedPrinter.getId())) {
|
||||||
|
mPrinters.put(updatedPrinter.getId(), updatedPrinter);
|
||||||
|
updatedPrinters = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updatedPrinters) {
|
||||||
|
deliverResult(new ArrayList<PrinterInfo>(mPrinters.values()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class PersistenceManager {
|
||||||
|
private static final String PERSIST_FILE_NAME = "printer_history.xml";
|
||||||
|
|
||||||
|
private static final String TAG_PRINTERS = "printers";
|
||||||
|
|
||||||
|
private static final String TAG_PRINTER = "printer";
|
||||||
|
private static final String TAG_PRINTER_ID = "printerId";
|
||||||
|
|
||||||
|
private static final String ATTR_LOCAL_ID = "localId";
|
||||||
|
private static final String ATTR_SERVICE_NAME = "serviceName";
|
||||||
|
|
||||||
|
private static final String ATTR_NAME = "name";
|
||||||
|
private static final String ATTR_DESCRIPTION = "description";
|
||||||
|
private static final String ATTR_STATUS = "status";
|
||||||
|
|
||||||
|
private final AtomicFile mStatePersistFile;
|
||||||
|
|
||||||
|
private List<PrinterInfo> mHistoricalPrinters;
|
||||||
|
|
||||||
|
private boolean mReadHistoryCompleted;
|
||||||
|
private boolean mReadHistoryInProgress;
|
||||||
|
|
||||||
|
private final AsyncTask<Void, Void, List<PrinterInfo>> mReadTask =
|
||||||
|
new AsyncTask<Void, Void, List<PrinterInfo>>() {
|
||||||
|
@Override
|
||||||
|
protected List<PrinterInfo> doInBackground(Void... args) {
|
||||||
|
return doReadPrinterHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(List<PrinterInfo> printers) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "read history completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mHistoricalPrinters = printers;
|
||||||
|
|
||||||
|
// Compute the favorite printers.
|
||||||
|
mFavoritePrinters = computeFavoritePrinters(printers);
|
||||||
|
|
||||||
|
// We want the first few favorite printers on top of the list.
|
||||||
|
final int favoriteCount = Math.min(mFavoritePrinters.size(),
|
||||||
|
MAX_HISTORICAL_PRINTER_COUNT);
|
||||||
|
for (int i = 0; i < favoriteCount; i++) {
|
||||||
|
PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
|
||||||
|
mPrinters.put(favoritePrinter.getId(), favoritePrinter);
|
||||||
|
}
|
||||||
|
|
||||||
|
mReadHistoryInProgress = false;
|
||||||
|
mReadHistoryCompleted = true;
|
||||||
|
|
||||||
|
loadInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PrinterInfo> doReadPrinterHistory() {
|
||||||
|
FileInputStream in = null;
|
||||||
|
try {
|
||||||
|
in = mStatePersistFile.openRead();
|
||||||
|
} catch (FileNotFoundException fnfe) {
|
||||||
|
Log.i(LOG_TAG, "No existing printer history.");
|
||||||
|
return new ArrayList<PrinterInfo>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setInput(in, null);
|
||||||
|
parseState(parser, printers);
|
||||||
|
return printers;
|
||||||
|
} catch (IllegalStateException ise) {
|
||||||
|
Slog.w(LOG_TAG, "Failed parsing ", ise);
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
Slog.w(LOG_TAG, "Failed parsing ", npe);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Slog.w(LOG_TAG, "Failed parsing ", nfe);
|
||||||
|
} catch (XmlPullParserException xppe) {
|
||||||
|
Slog.w(LOG_TAG, "Failed parsing ", xppe);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Slog.w(LOG_TAG, "Failed parsing ", ioe);
|
||||||
|
} catch (IndexOutOfBoundsException iobe) {
|
||||||
|
Slog.w(LOG_TAG, "Failed parsing ", iobe);
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseState(XmlPullParser parser, List<PrinterInfo> outPrinters)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
parser.next();
|
||||||
|
skipEmptyTextTags(parser);
|
||||||
|
expect(parser, XmlPullParser.START_TAG, TAG_PRINTERS);
|
||||||
|
parser.next();
|
||||||
|
|
||||||
|
while (parsePrinter(parser, outPrinters)) {
|
||||||
|
// Be nice and respond to cancellation
|
||||||
|
if (isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
skipEmptyTextTags(parser);
|
||||||
|
expect(parser, XmlPullParser.END_TAG, TAG_PRINTERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parsePrinter(XmlPullParser parser, List<PrinterInfo> outPrinters)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
skipEmptyTextTags(parser);
|
||||||
|
if (!accept(parser, XmlPullParser.START_TAG, TAG_PRINTER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||||
|
String description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
|
||||||
|
final int status = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATUS));
|
||||||
|
|
||||||
|
parser.next();
|
||||||
|
|
||||||
|
skipEmptyTextTags(parser);
|
||||||
|
expect(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID);
|
||||||
|
String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
|
||||||
|
ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
|
||||||
|
null, ATTR_SERVICE_NAME));
|
||||||
|
PrinterId printerId = new PrinterId(service, localId);
|
||||||
|
parser.next();
|
||||||
|
skipEmptyTextTags(parser);
|
||||||
|
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
|
||||||
|
parser.next();
|
||||||
|
|
||||||
|
PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, name, status);
|
||||||
|
builder.setDescription(description);
|
||||||
|
PrinterInfo printer = builder.create();
|
||||||
|
|
||||||
|
outPrinters.add(printer);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "[RESTORED] " + printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipEmptyTextTags(parser);
|
||||||
|
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expect(XmlPullParser parser, int type, String tag)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
if (!accept(parser, type, tag)) {
|
||||||
|
throw new XmlPullParserException("Exepected event: " + type
|
||||||
|
+ " and tag: " + tag + " but got event: " + parser.getEventType()
|
||||||
|
+ " and tag:" + parser.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipEmptyTextTags(XmlPullParser parser)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
while (accept(parser, XmlPullParser.TEXT, null)
|
||||||
|
&& "\n".equals(parser.getText())) {
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean accept(XmlPullParser parser, int type, String tag)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
if (parser.getEventType() != type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tag != null) {
|
||||||
|
if (!tag.equals(parser.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (parser.getName() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final AsyncTask<List<PrinterInfo>, Void, Void> mWriteTask =
|
||||||
|
new AsyncTask<List<PrinterInfo>, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(List<PrinterInfo>... printers) {
|
||||||
|
doWritePrinterHistory(printers[0]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doWritePrinterHistory(List<PrinterInfo> printers) {
|
||||||
|
FileOutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = mStatePersistFile.startWrite();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new FastXmlSerializer();
|
||||||
|
serializer.setOutput(out, "utf-8");
|
||||||
|
serializer.startDocument(null, true);
|
||||||
|
serializer.startTag(null, TAG_PRINTERS);
|
||||||
|
|
||||||
|
final int printerCount = printers.size();
|
||||||
|
for (int i = 0; i < printerCount; i++) {
|
||||||
|
PrinterInfo printer = printers.get(i);
|
||||||
|
|
||||||
|
serializer.startTag(null, TAG_PRINTER);
|
||||||
|
|
||||||
|
serializer.attribute(null, ATTR_NAME, printer.getName());
|
||||||
|
serializer.attribute(null, ATTR_STATUS, String.valueOf(
|
||||||
|
printer.getStatus()));
|
||||||
|
String description = printer.getDescription();
|
||||||
|
if (description != null) {
|
||||||
|
serializer.attribute(null, ATTR_DESCRIPTION, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrinterId printerId = printer.getId();
|
||||||
|
serializer.startTag(null, TAG_PRINTER_ID);
|
||||||
|
serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
|
||||||
|
serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
|
||||||
|
.flattenToString());
|
||||||
|
serializer.endTag(null, TAG_PRINTER_ID);
|
||||||
|
|
||||||
|
serializer.endTag(null, TAG_PRINTER);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "[PERSISTED] " + printer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer.endTag(null, TAG_PRINTERS);
|
||||||
|
serializer.endDocument();
|
||||||
|
mStatePersistFile.finishWrite(out);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "[PERSIST END]");
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Slog.w(LOG_TAG, "Failed to write printer history, restoring backup.", ioe);
|
||||||
|
mStatePersistFile.failWrite(out);
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private PersistenceManager(Context context) {
|
||||||
|
mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
|
||||||
|
PERSIST_FILE_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReadHistoryInProgress() {
|
||||||
|
return mReadHistoryInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReadHistoryCompleted() {
|
||||||
|
return mReadHistoryCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean stopReadPrinterHistory() {
|
||||||
|
return mReadTask.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readPrinterHistory() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "read history started");
|
||||||
|
}
|
||||||
|
mReadHistoryInProgress = true;
|
||||||
|
mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void addPrinterAndWritePrinterHistory(PrinterInfo printer) {
|
||||||
|
if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
|
||||||
|
mHistoricalPrinters.remove(0);
|
||||||
|
}
|
||||||
|
mHistoricalPrinters.add(printer);
|
||||||
|
mWriteTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, mHistoricalPrinters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
|
||||||
|
Map<PrinterId, PrinterRecord> recordMap =
|
||||||
|
new ArrayMap<PrinterId, PrinterRecord>();
|
||||||
|
|
||||||
|
// Recompute the weights.
|
||||||
|
float currentWeight = 1.0f;
|
||||||
|
final int printerCount = printers.size();
|
||||||
|
for (int i = printerCount - 1; i >= 0; i--) {
|
||||||
|
PrinterInfo printer = printers.get(i);
|
||||||
|
// Aggregate weight for the same printer
|
||||||
|
PrinterRecord record = recordMap.get(printer.getId());
|
||||||
|
if (record == null) {
|
||||||
|
record = new PrinterRecord(printer);
|
||||||
|
recordMap.put(printer.getId(), record);
|
||||||
|
}
|
||||||
|
record.weight += currentWeight;
|
||||||
|
currentWeight *= WEIGHT_DECAY_COEFFICIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft the favorite printers.
|
||||||
|
List<PrinterRecord> favoriteRecords = new ArrayList<PrinterRecord>(
|
||||||
|
recordMap.values());
|
||||||
|
Collections.sort(favoriteRecords);
|
||||||
|
|
||||||
|
// Write the favorites to the output.
|
||||||
|
final int favoriteCount = favoriteRecords.size();
|
||||||
|
List<PrinterInfo> favoritePrinters = new ArrayList<PrinterInfo>(favoriteCount);
|
||||||
|
for (int i = 0; i < favoriteCount; i++) {
|
||||||
|
PrinterInfo printer = favoriteRecords.get(i).printer;
|
||||||
|
favoritePrinters.add(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return favoritePrinters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class PrinterRecord implements Comparable<PrinterRecord> {
|
||||||
|
public final PrinterInfo printer;
|
||||||
|
public float weight;
|
||||||
|
|
||||||
|
public PrinterRecord(PrinterInfo printer) {
|
||||||
|
this.printer = printer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(PrinterRecord another) {
|
||||||
|
return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,969 +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 com.android.printspooler;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
import android.print.IPrintClient;
|
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PageRange;
|
|
||||||
import android.print.PrintAttributes;
|
|
||||||
import android.print.PrintAttributes.Margins;
|
|
||||||
import android.print.PrintAttributes.MediaSize;
|
|
||||||
import android.print.PrintAttributes.Resolution;
|
|
||||||
import android.print.PrintAttributes.Tray;
|
|
||||||
import android.print.PrintDocumentInfo;
|
|
||||||
import android.print.PrintJobInfo;
|
|
||||||
import android.print.PrintManager;
|
|
||||||
import android.print.PrinterId;
|
|
||||||
import android.print.PrinterInfo;
|
|
||||||
import android.util.AtomicFile;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Slog;
|
|
||||||
import android.util.Xml;
|
|
||||||
|
|
||||||
import com.android.internal.util.FastXmlSerializer;
|
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PrintSpooler {
|
|
||||||
|
|
||||||
private static final String LOG_TAG = "PrintSpooler";
|
|
||||||
|
|
||||||
private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = true;
|
|
||||||
|
|
||||||
private static final boolean DEBUG_PERSISTENCE = true;
|
|
||||||
|
|
||||||
private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
|
|
||||||
|
|
||||||
private static final String PRINT_FILE_EXTENSION = "pdf";
|
|
||||||
|
|
||||||
private static int sPrintJobIdCounter;
|
|
||||||
|
|
||||||
private static final Object sLock = new Object();
|
|
||||||
|
|
||||||
private static PrintSpooler sInstance;
|
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
|
||||||
|
|
||||||
private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
|
|
||||||
|
|
||||||
private final PersistenceManager mPersistanceManager;
|
|
||||||
|
|
||||||
private final NotificationController mNotificationController;
|
|
||||||
|
|
||||||
private final PrintSpoolerService mService;
|
|
||||||
|
|
||||||
public static void destroyInstance() {
|
|
||||||
synchronized (sLock) {
|
|
||||||
sInstance = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void createInstance(PrintSpoolerService service) {
|
|
||||||
synchronized (sLock) {
|
|
||||||
sInstance = new PrintSpooler(service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PrintSpooler peekInstance() {
|
|
||||||
synchronized (sLock) {
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PrintSpooler(PrintSpoolerService service) {
|
|
||||||
mService = service;
|
|
||||||
mPersistanceManager = new PersistenceManager(service);
|
|
||||||
mNotificationController = new NotificationController(service);
|
|
||||||
synchronized (mLock) {
|
|
||||||
mPersistanceManager.readStateLocked();
|
|
||||||
handleReadPrintJobsLocked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
|
|
||||||
int state, int appId) {
|
|
||||||
List<PrintJobInfo> foundPrintJobs = null;
|
|
||||||
synchronized (mLock) {
|
|
||||||
final int printJobCount = mPrintJobs.size();
|
|
||||||
for (int i = 0; i < printJobCount; i++) {
|
|
||||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
|
||||||
PrinterId printerId = printJob.getPrinterId();
|
|
||||||
final boolean sameComponent = (componentName == null
|
|
||||||
|| (printerId != null
|
|
||||||
&& componentName.equals(printerId.getServiceName())));
|
|
||||||
final boolean sameAppId = appId == PrintManager.APP_ID_ANY
|
|
||||||
|| printJob.getAppId() == appId;
|
|
||||||
final boolean sameState = (state == printJob.getState())
|
|
||||||
|| (state == PrintJobInfo.STATE_ANY)
|
|
||||||
|| (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
|
|
||||||
&& printJob.getState() > PrintJobInfo.STATE_CREATED);
|
|
||||||
if (sameComponent && sameAppId && sameState) {
|
|
||||||
if (foundPrintJobs == null) {
|
|
||||||
foundPrintJobs = new ArrayList<PrintJobInfo>();
|
|
||||||
}
|
|
||||||
foundPrintJobs.add(printJob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return foundPrintJobs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
final int printJobCount = mPrintJobs.size();
|
|
||||||
for (int i = 0; i < printJobCount; i++) {
|
|
||||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
|
||||||
if (printJob.getId() == printJobId
|
|
||||||
&& (appId == PrintManager.APP_ID_ANY
|
|
||||||
|| appId == printJob.getAppId())) {
|
|
||||||
return printJob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrintJobInfo createPrintJob(CharSequence label, IPrintClient client,
|
|
||||||
PrintAttributes attributes, int appId) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
final int printJobId = generatePrintJobIdLocked();
|
|
||||||
PrintJobInfo printJob = new PrintJobInfo();
|
|
||||||
printJob.setId(printJobId);
|
|
||||||
printJob.setAppId(appId);
|
|
||||||
printJob.setLabel(label);
|
|
||||||
printJob.setAttributes(attributes);
|
|
||||||
printJob.setState(PrintJobInfo.STATE_CREATED);
|
|
||||||
|
|
||||||
addPrintJobLocked(printJob);
|
|
||||||
|
|
||||||
return printJob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleReadPrintJobsLocked() {
|
|
||||||
final int printJobCount = mPrintJobs.size();
|
|
||||||
for (int i = 0; i < printJobCount; i++) {
|
|
||||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
|
||||||
|
|
||||||
// Update the notification.
|
|
||||||
mNotificationController.onPrintJobStateChanged(printJob);
|
|
||||||
|
|
||||||
switch (printJob.getState()) {
|
|
||||||
case PrintJobInfo.STATE_QUEUED:
|
|
||||||
case PrintJobInfo.STATE_STARTED: {
|
|
||||||
// We have a print job that was queued or started in the past
|
|
||||||
// but the device battery died or a crash occurred. In this case
|
|
||||||
// we assume the print job failed and let the user decide whether
|
|
||||||
// to restart the job or just
|
|
||||||
setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
|
|
||||||
mService.getString(R.string.no_connection_to_printer));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkAllPrintJobsHandled() {
|
|
||||||
synchronized (mLock) {
|
|
||||||
if (!hasActivePrintJobsLocked()) {
|
|
||||||
notifyOnAllPrintJobsHandled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
|
|
||||||
mService.createPrinterDiscoverySession(observer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int generatePrintJobIdLocked() {
|
|
||||||
int printJobId = sPrintJobIdCounter++;
|
|
||||||
while (isDuplicatePrintJobId(printJobId)) {
|
|
||||||
printJobId = sPrintJobIdCounter++;
|
|
||||||
}
|
|
||||||
return printJobId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDuplicatePrintJobId(int printJobId) {
|
|
||||||
final int printJobCount = mPrintJobs.size();
|
|
||||||
for (int j = 0; j < printJobCount; j++) {
|
|
||||||
PrintJobInfo printJob = mPrintJobs.get(j);
|
|
||||||
if (printJob.getId() == printJobId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writePrintJobData(final ParcelFileDescriptor fd, final int printJobId) {
|
|
||||||
final PrintJobInfo printJob;
|
|
||||||
synchronized (mLock) {
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
Log.i(LOG_TAG, "[END WRITE]");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public File generateFileForPrintJob(int printJobId) {
|
|
||||||
return new File(mService.getFilesDir(), "print_job_"
|
|
||||||
+ printJobId + "." + PRINT_FILE_EXTENSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPrintJobLocked(PrintJobInfo printJob) {
|
|
||||||
mPrintJobs.add(printJob);
|
|
||||||
if (DEBUG_PRINT_JOB_LIFECYCLE) {
|
|
||||||
Slog.i(LOG_TAG, "[ADD] " + printJob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removePrintJobLocked(PrintJobInfo printJob) {
|
|
||||||
if (mPrintJobs.remove(printJob)) {
|
|
||||||
generateFileForPrintJob(printJob.getId()).delete();
|
|
||||||
if (DEBUG_PRINT_JOB_LIFECYCLE) {
|
|
||||||
Slog.i(LOG_TAG, "[REMOVE] " + printJob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean setPrintJobState(int printJobId, int state, CharSequence error) {
|
|
||||||
boolean success = false;
|
|
||||||
|
|
||||||
synchronized (mLock) {
|
|
||||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
|
||||||
if (printJob != null) {
|
|
||||||
success = true;
|
|
||||||
|
|
||||||
printJob.setState(state);
|
|
||||||
printJob.setFailureReason(error);
|
|
||||||
mNotificationController.onPrintJobStateChanged(printJob);
|
|
||||||
|
|
||||||
if (DEBUG_PRINT_JOB_LIFECYCLE) {
|
|
||||||
Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case PrintJobInfo.STATE_COMPLETED:
|
|
||||||
case PrintJobInfo.STATE_CANCELED:
|
|
||||||
removePrintJobLocked(printJob);
|
|
||||||
// $fall-through$
|
|
||||||
case PrintJobInfo.STATE_FAILED: {
|
|
||||||
PrinterId printerId = printJob.getPrinterId();
|
|
||||||
if (printerId != null) {
|
|
||||||
ComponentName service = printerId.getServiceName();
|
|
||||||
if (!hasActivePrintJobsForServiceLocked(service)) {
|
|
||||||
mService.onAllPrintJobsForServiceHandled(service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case PrintJobInfo.STATE_QUEUED: {
|
|
||||||
mService.onPrintJobQueued(new PrintJobInfo(printJob));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldPersistPrintJob(printJob)) {
|
|
||||||
mPersistanceManager.writeStateLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasActivePrintJobsLocked()) {
|
|
||||||
notifyOnAllPrintJobsHandled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasActivePrintJobsLocked() {
|
|
||||||
final int printJobCount = mPrintJobs.size();
|
|
||||||
for (int i = 0; i < printJobCount; i++) {
|
|
||||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
|
||||||
if (isActiveState(printJob.getState())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
|
|
||||||
final int printJobCount = mPrintJobs.size();
|
|
||||||
for (int i = 0; i < printJobCount; i++) {
|
|
||||||
PrintJobInfo printJob = mPrintJobs.get(i);
|
|
||||||
if (isActiveState(printJob.getState())
|
|
||||||
&& printJob.getPrinterId().getServiceName().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);
|
|
||||||
if (shouldPersistPrintJob(printJob)) {
|
|
||||||
mPersistanceManager.writeStateLocked();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrintJobAttributesNoPersistence(int printJobId, PrintAttributes attributes) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
|
||||||
if (printJob != null) {
|
|
||||||
printJob.setAttributes(attributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrintJobPrinterNoPersistence(int printJobId, PrinterInfo printer) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
|
||||||
if (printJob != null) {
|
|
||||||
printJob.setPrinterId(printer.getId());
|
|
||||||
printJob.setPrinterName(printer.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrintJobPagesNoPersistence(int printJobId, PageRange[] pages) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
|
|
||||||
if (printJob != null) {
|
|
||||||
printJob.setPages(pages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
|
|
||||||
return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyOnAllPrintJobsHandled() {
|
|
||||||
// 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) {
|
|
||||||
mService.onAllPrintJobsHandled();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class PersistenceManager {
|
|
||||||
private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
|
|
||||||
|
|
||||||
private static final String TAG_SPOOLER = "spooler";
|
|
||||||
private static final String TAG_JOB = "job";
|
|
||||||
|
|
||||||
private static final String TAG_PRINTER_ID = "printerId";
|
|
||||||
private static final String TAG_PAGE_RANGE = "pageRange";
|
|
||||||
private static final String TAG_ATTRIBUTES = "attributes";
|
|
||||||
private static final String TAG_DOCUMENT_INFO = "documentInfo";
|
|
||||||
|
|
||||||
private static final String ATTR_ID = "id";
|
|
||||||
private static final String ATTR_LABEL = "label";
|
|
||||||
private static final String ATTR_STATE = "state";
|
|
||||||
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";
|
|
||||||
private static final String TAG_MARGINS = "margins";
|
|
||||||
private static final String TAG_INPUT_TRAY = "inputTray";
|
|
||||||
private static final String TAG_OUTPUT_TRAY = "outputTray";
|
|
||||||
|
|
||||||
private static final String ATTR_DUPLEX_MODE = "duplexMode";
|
|
||||||
private static final String ATTR_COLOR_MODE = "colorMode";
|
|
||||||
private static final String ATTR_FITTING_MODE = "fittingMode";
|
|
||||||
private static final String ATTR_ORIENTATION = "orientation";
|
|
||||||
|
|
||||||
private static final String ATTR_LOCAL_ID = "printerName";
|
|
||||||
private static final String ATTR_SERVICE_NAME = "serviceName";
|
|
||||||
|
|
||||||
private static final String ATTR_WIDTH_MILS = "widthMils";
|
|
||||||
private static final String ATTR_HEIGHT_MILS = "heightMils";
|
|
||||||
|
|
||||||
private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
|
|
||||||
private static final String ATTR_VERTICAL_DPI = "verticalDpi";
|
|
||||||
|
|
||||||
private static final String ATTR_LEFT_MILS = "leftMils";
|
|
||||||
private static final String ATTR_TOP_MILS = "topMils";
|
|
||||||
private static final String ATTR_RIGHT_MILS = "rightMils";
|
|
||||||
private static final String ATTR_BOTTOM_MILS = "bottomMils";
|
|
||||||
|
|
||||||
private static final String ATTR_START = "start";
|
|
||||||
private static final String ATTR_END = "end";
|
|
||||||
|
|
||||||
private static final String ATTR_NAME = "name";
|
|
||||||
private static final String ATTR_PAGE_COUNT = "pageCount";
|
|
||||||
private static final String ATTR_CONTENT_TYPE = "contentType";
|
|
||||||
|
|
||||||
private final AtomicFile mStatePersistFile;
|
|
||||||
|
|
||||||
private boolean mWriteStateScheduled;
|
|
||||||
|
|
||||||
private PersistenceManager(Context context) {
|
|
||||||
mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
|
|
||||||
PERSIST_FILE_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeStateLocked() {
|
|
||||||
if (!PERSISTNECE_MANAGER_ENABLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mWriteStateScheduled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mWriteStateScheduled = true;
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params) {
|
|
||||||
synchronized (mLock) {
|
|
||||||
mWriteStateScheduled = false;
|
|
||||||
doWriteStateLocked();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doWriteStateLocked() {
|
|
||||||
if (DEBUG_PERSISTENCE) {
|
|
||||||
Log.i(LOG_TAG, "[PERSIST START]");
|
|
||||||
}
|
|
||||||
FileOutputStream out = null;
|
|
||||||
try {
|
|
||||||
out = mStatePersistFile.startWrite();
|
|
||||||
|
|
||||||
XmlSerializer serializer = new FastXmlSerializer();
|
|
||||||
serializer.setOutput(out, "utf-8");
|
|
||||||
serializer.startDocument(null, true);
|
|
||||||
serializer.startTag(null, TAG_SPOOLER);
|
|
||||||
|
|
||||||
List<PrintJobInfo> printJobs = mPrintJobs;
|
|
||||||
|
|
||||||
final int printJobCount = printJobs.size();
|
|
||||||
for (int j = 0; j < printJobCount; j++) {
|
|
||||||
PrintJobInfo printJob = printJobs.get(j);
|
|
||||||
|
|
||||||
final int state = printJob.getState();
|
|
||||||
if (state < PrintJobInfo.STATE_QUEUED
|
|
||||||
|| state > PrintJobInfo.STATE_CANCELED) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer.startTag(null, TAG_JOB);
|
|
||||||
|
|
||||||
serializer.attribute(null, ATTR_ID, String.valueOf(printJob.getId()));
|
|
||||||
serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
|
|
||||||
serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
|
|
||||||
serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
|
|
||||||
serializer.attribute(null, ATTR_USER_ID, String.valueOf(printJob.getUserId()));
|
|
||||||
String tag = printJob.getTag();
|
|
||||||
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) {
|
|
||||||
serializer.startTag(null, TAG_PRINTER_ID);
|
|
||||||
serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
|
|
||||||
serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
|
|
||||||
.flattenToString());
|
|
||||||
serializer.endTag(null, TAG_PRINTER_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
PageRange[] pages = printJob.getPages();
|
|
||||||
if (pages != null) {
|
|
||||||
for (int i = 0; i < pages.length; i++) {
|
|
||||||
serializer.startTag(null, TAG_PAGE_RANGE);
|
|
||||||
serializer.attribute(null, ATTR_START, String.valueOf(
|
|
||||||
pages[i].getStart()));
|
|
||||||
serializer.attribute(null, ATTR_END, String.valueOf(
|
|
||||||
pages[i].getEnd()));
|
|
||||||
serializer.endTag(null, TAG_PAGE_RANGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintAttributes attributes = printJob.getAttributes();
|
|
||||||
if (attributes != null) {
|
|
||||||
serializer.startTag(null, TAG_ATTRIBUTES);
|
|
||||||
|
|
||||||
final int duplexMode = attributes.getDuplexMode();
|
|
||||||
serializer.attribute(null, ATTR_DUPLEX_MODE,
|
|
||||||
String.valueOf(duplexMode));
|
|
||||||
|
|
||||||
final int colorMode = attributes.getColorMode();
|
|
||||||
serializer.attribute(null, ATTR_COLOR_MODE,
|
|
||||||
String.valueOf(colorMode));
|
|
||||||
|
|
||||||
final int fittingMode = attributes.getFittingMode();
|
|
||||||
serializer.attribute(null, ATTR_FITTING_MODE,
|
|
||||||
String.valueOf(fittingMode));
|
|
||||||
|
|
||||||
final int orientation = attributes.getOrientation();
|
|
||||||
serializer.attribute(null, ATTR_ORIENTATION,
|
|
||||||
String.valueOf(orientation));
|
|
||||||
|
|
||||||
MediaSize mediaSize = attributes.getMediaSize();
|
|
||||||
if (mediaSize != null) {
|
|
||||||
serializer.startTag(null, TAG_MEDIA_SIZE);
|
|
||||||
serializer.attribute(null, ATTR_ID, mediaSize.getId());
|
|
||||||
serializer.attribute(null, ATTR_LABEL, mediaSize.getLabel()
|
|
||||||
.toString());
|
|
||||||
serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
|
|
||||||
mediaSize.getWidthMils()));
|
|
||||||
serializer.attribute(null, ATTR_HEIGHT_MILS,String.valueOf(
|
|
||||||
mediaSize.getHeightMils()));
|
|
||||||
serializer.endTag(null, TAG_MEDIA_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Resolution resolution = attributes.getResolution();
|
|
||||||
if (resolution != null) {
|
|
||||||
serializer.startTag(null, TAG_RESOLUTION);
|
|
||||||
serializer.attribute(null, ATTR_ID, resolution.getId());
|
|
||||||
serializer.attribute(null, ATTR_LABEL, resolution.getLabel()
|
|
||||||
.toString());
|
|
||||||
serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
|
|
||||||
resolution.getHorizontalDpi()));
|
|
||||||
serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
|
|
||||||
resolution.getVerticalDpi()));
|
|
||||||
serializer.endTag(null, TAG_RESOLUTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
Margins margins = attributes.getMargins();
|
|
||||||
if (margins != null) {
|
|
||||||
serializer.startTag(null, TAG_MARGINS);
|
|
||||||
serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
|
|
||||||
margins.getLeftMils()));
|
|
||||||
serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
|
|
||||||
margins.getTopMils()));
|
|
||||||
serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
|
|
||||||
margins.getRightMils()));
|
|
||||||
serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
|
|
||||||
margins.getBottomMils()));
|
|
||||||
serializer.endTag(null, TAG_MARGINS);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tray inputTray = attributes.getInputTray();
|
|
||||||
if (inputTray != null) {
|
|
||||||
serializer.startTag(null, TAG_INPUT_TRAY);
|
|
||||||
serializer.attribute(null, ATTR_ID, inputTray.getId());
|
|
||||||
serializer.attribute(null, ATTR_LABEL, inputTray.getLabel()
|
|
||||||
.toString());
|
|
||||||
serializer.endTag(null, TAG_INPUT_TRAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tray outputTray = attributes.getOutputTray();
|
|
||||||
if (outputTray != null) {
|
|
||||||
serializer.startTag(null, TAG_OUTPUT_TRAY);
|
|
||||||
serializer.attribute(null, ATTR_ID, outputTray.getId());
|
|
||||||
serializer.attribute(null, ATTR_LABEL, outputTray.getLabel()
|
|
||||||
.toString());
|
|
||||||
serializer.endTag(null, TAG_OUTPUT_TRAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer.endTag(null, TAG_ATTRIBUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
|
|
||||||
if (documentInfo != null) {
|
|
||||||
serializer.startTag(null, TAG_DOCUMENT_INFO);
|
|
||||||
serializer.attribute(null, ATTR_NAME, documentInfo.getName());
|
|
||||||
serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
|
|
||||||
documentInfo.getContentType()));
|
|
||||||
serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
|
|
||||||
documentInfo.getPageCount()));
|
|
||||||
serializer.endTag(null, TAG_DOCUMENT_INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer.endTag(null, TAG_JOB);
|
|
||||||
|
|
||||||
if (DEBUG_PERSISTENCE) {
|
|
||||||
Log.i(LOG_TAG, "[PERSISTED] " + printJob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
} finally {
|
|
||||||
IoUtils.closeQuietly(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readStateLocked() {
|
|
||||||
if (!PERSISTNECE_MANAGER_ENABLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FileInputStream in = null;
|
|
||||||
try {
|
|
||||||
in = mStatePersistFile.openRead();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.i(LOG_TAG, "No existing print spooler state.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
|
||||||
parser.setInput(in, null);
|
|
||||||
parseState(parser);
|
|
||||||
} catch (IllegalStateException ise) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", ise);
|
|
||||||
} catch (NullPointerException npe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", npe);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", nfe);
|
|
||||||
} catch (XmlPullParserException xppe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", xppe);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", ioe);
|
|
||||||
} catch (IndexOutOfBoundsException iobe) {
|
|
||||||
Slog.w(LOG_TAG, "Failed parsing ", iobe);
|
|
||||||
} finally {
|
|
||||||
IoUtils.closeQuietly(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseState(XmlPullParser parser)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
|
|
||||||
parser.next();
|
|
||||||
|
|
||||||
while (parsePrintJob(parser)) {
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean parsePrintJob(XmlPullParser parser)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintJobInfo printJob = new PrintJobInfo();
|
|
||||||
|
|
||||||
final int printJobId = Integer.parseInt(parser.getAttributeValue(null, ATTR_ID));
|
|
||||||
printJob.setId(printJobId);
|
|
||||||
String label = parser.getAttributeValue(null, ATTR_LABEL);
|
|
||||||
printJob.setLabel(label);
|
|
||||||
final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
|
|
||||||
printJob.setState(state);
|
|
||||||
final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
|
|
||||||
printJob.setAppId(appId);
|
|
||||||
final int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USER_ID));
|
|
||||||
printJob.setUserId(userId);
|
|
||||||
String tag = parser.getAttributeValue(null, ATTR_TAG);
|
|
||||||
printJob.setTag(tag);
|
|
||||||
String copies = parser.getAttributeValue(null, ATTR_COPIES);
|
|
||||||
printJob.setCopies(Integer.parseInt(copies));
|
|
||||||
|
|
||||||
parser.next();
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
|
|
||||||
String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
|
|
||||||
ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
|
|
||||||
null, ATTR_SERVICE_NAME));
|
|
||||||
printJob.setPrinterId(new PrinterId(service, localId));
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
List<PageRange> pageRanges = null;
|
|
||||||
while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
|
|
||||||
final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
|
|
||||||
final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
|
|
||||||
PageRange pageRange = new PageRange(start, end);
|
|
||||||
if (pageRanges == null) {
|
|
||||||
pageRanges = new ArrayList<PageRange>();
|
|
||||||
}
|
|
||||||
pageRanges.add(pageRange);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
if (pageRanges != null) {
|
|
||||||
PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
|
|
||||||
pageRanges.toArray(pageRangesArray);
|
|
||||||
printJob.setPages(pageRangesArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
|
|
||||||
|
|
||||||
PrintAttributes.Builder builder = new PrintAttributes.Builder();
|
|
||||||
|
|
||||||
String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
|
|
||||||
builder.setDuplexMode(Integer.parseInt(duplexMode));
|
|
||||||
|
|
||||||
String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
|
|
||||||
builder.setColorMode(Integer.parseInt(colorMode));
|
|
||||||
|
|
||||||
String fittingMode = parser.getAttributeValue(null, ATTR_FITTING_MODE);
|
|
||||||
builder.setFittingMode(Integer.parseInt(fittingMode));
|
|
||||||
|
|
||||||
String orientation = parser.getAttributeValue(null, ATTR_ORIENTATION);
|
|
||||||
builder.setOrientation(Integer.parseInt(orientation));
|
|
||||||
|
|
||||||
parser.next();
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
|
|
||||||
String id = parser.getAttributeValue(null, ATTR_ID);
|
|
||||||
label = parser.getAttributeValue(null, ATTR_LABEL);
|
|
||||||
final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_WIDTH_MILS));
|
|
||||||
final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_HEIGHT_MILS));
|
|
||||||
MediaSize mediaSize = new MediaSize(id, label, widthMils, heightMils);
|
|
||||||
builder.setMediaSize(mediaSize);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
|
|
||||||
String id = parser.getAttributeValue(null, ATTR_ID);
|
|
||||||
label = parser.getAttributeValue(null, ATTR_LABEL);
|
|
||||||
final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_HORIZONTAL_DPI));
|
|
||||||
final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_VERTICAL_DPI));
|
|
||||||
Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
|
|
||||||
builder.setResolution(resolution);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
|
|
||||||
final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_LEFT_MILS));
|
|
||||||
final int topMils = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_TOP_MILS));
|
|
||||||
final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_RIGHT_MILS));
|
|
||||||
final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_BOTTOM_MILS));
|
|
||||||
Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
|
|
||||||
builder.setMargins(margins);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_INPUT_TRAY)) {
|
|
||||||
String id = parser.getAttributeValue(null, ATTR_ID);
|
|
||||||
label = parser.getAttributeValue(null, ATTR_LABEL);
|
|
||||||
Tray tray = new Tray(id, label);
|
|
||||||
builder.setInputTray(tray);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_INPUT_TRAY);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_OUTPUT_TRAY)) {
|
|
||||||
String id = parser.getAttributeValue(null, ATTR_ID);
|
|
||||||
label = parser.getAttributeValue(null, ATTR_LABEL);
|
|
||||||
Tray tray = new Tray(id, label);
|
|
||||||
builder.setOutputTray(tray);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_OUTPUT_TRAY);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
printJob.setAttributes(builder.create());
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
|
|
||||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
|
||||||
final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_PAGE_COUNT));
|
|
||||||
final int contentType = Integer.parseInt(parser.getAttributeValue(null,
|
|
||||||
ATTR_CONTENT_TYPE));
|
|
||||||
PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
|
|
||||||
.setPageCount(pageCount)
|
|
||||||
.setContentType(contentType).create();
|
|
||||||
printJob.setDocumentInfo(info);
|
|
||||||
parser.next();
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
mPrintJobs.add(printJob);
|
|
||||||
|
|
||||||
if (DEBUG_PERSISTENCE) {
|
|
||||||
Log.i(LOG_TAG, "[RESTORED] " + printJob);
|
|
||||||
}
|
|
||||||
|
|
||||||
skipEmptyTextTags(parser);
|
|
||||||
expect(parser, XmlPullParser.END_TAG, TAG_JOB);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expect(XmlPullParser parser, int type, String tag)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
if (!accept(parser, type, tag)) {
|
|
||||||
throw new XmlPullParserException("Exepected event: " + type
|
|
||||||
+ " and tag: " + tag + " but got event: " + parser.getEventType()
|
|
||||||
+ " and tag:" + parser.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void skipEmptyTextTags(XmlPullParser parser)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
while (accept(parser, XmlPullParser.TEXT, null)
|
|
||||||
&& "\n".equals(parser.getText())) {
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean accept(XmlPullParser parser, int type, String tag)
|
|
||||||
throws IOException, XmlPullParserException {
|
|
||||||
if (parser.getEventType() != type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tag != null) {
|
|
||||||
if (!tag.equals(parser.getName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (parser.getName() != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -17,12 +17,25 @@
|
|||||||
package com.android.printspooler;
|
package com.android.printspooler;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.print.PrinterId;
|
||||||
|
|
||||||
public class ChoosePrinterActivity extends Activity {
|
import com.android.printspooler.SelectPrinterFragment.OnPrinterSelectedListener;
|
||||||
|
|
||||||
|
public class SelectPrinterActivity extends Activity implements OnPrinterSelectedListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
setContentView(R.layout.choose_printer_activity);
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.select_printer_activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrinterSelected(PrinterId printer) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(PrintJobConfigActivity.INTENT_EXTRA_PRINTER_ID, printer);
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.android.printspooler;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.DialogFragment;
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.app.FragmentTransaction;
|
||||||
|
import android.app.ListFragment;
|
||||||
|
import android.app.LoaderManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.Loader;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.print.PrinterId;
|
||||||
|
import android.print.PrinterInfo;
|
||||||
|
import android.printservice.PrintServiceInfo;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.Filter;
|
||||||
|
import android.widget.Filterable;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.SearchView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a fragment for selecting a printer.
|
||||||
|
*/
|
||||||
|
public final class SelectPrinterFragment extends ListFragment {
|
||||||
|
|
||||||
|
private static final int LOADER_ID_PRINTERS_LOADER = 1;
|
||||||
|
|
||||||
|
private static final String FRAGMRNT_TAG_ADD_PRINTER_DIALOG =
|
||||||
|
"FRAGMRNT_TAG_ADD_PRINTER_DIALOG";
|
||||||
|
|
||||||
|
private static final String FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS =
|
||||||
|
"FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS";
|
||||||
|
|
||||||
|
private final ArrayList<PrintServiceInfo> mAddPrinterServices =
|
||||||
|
new ArrayList<PrintServiceInfo>();
|
||||||
|
|
||||||
|
public static interface OnPrinterSelectedListener {
|
||||||
|
public void onPrinterSelected(PrinterId printerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
setListAdapter(new DestinationAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.select_printer_activity, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
|
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String searchString) {
|
||||||
|
((DestinationAdapter) getListAdapter()).getFilter().filter(searchString);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mAddPrinterServices.isEmpty()) {
|
||||||
|
menu.removeItem(R.id.action_add_printer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
updateAddPrintersAdapter();
|
||||||
|
getActivity().invalidateOptionsMenu();
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onListItemClick(ListView list, View view, int position, long id) {
|
||||||
|
PrinterInfo printer = (PrinterInfo) list.getAdapter().getItem(position);
|
||||||
|
Activity activity = getActivity();
|
||||||
|
if (activity instanceof OnPrinterSelectedListener) {
|
||||||
|
((OnPrinterSelectedListener) activity).onPrinterSelected(printer.getId());
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("the host activity must implement"
|
||||||
|
+ " OnPrinterSelectedListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_add_printer) {
|
||||||
|
showAddPrinterSelectionDialog();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAddPrintersAdapter() {
|
||||||
|
mAddPrinterServices.clear();
|
||||||
|
|
||||||
|
// Get all print services.
|
||||||
|
List<ResolveInfo> resolveInfos = getActivity().getPackageManager().queryIntentServices(
|
||||||
|
new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
|
||||||
|
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
|
||||||
|
|
||||||
|
// No print services - done.
|
||||||
|
if (resolveInfos.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the services with valid add printers activities.
|
||||||
|
final int resolveInfoCount = resolveInfos.size();
|
||||||
|
for (int i = 0; i < resolveInfoCount; i++) {
|
||||||
|
ResolveInfo resolveInfo = resolveInfos.get(i);
|
||||||
|
|
||||||
|
PrintServiceInfo printServiceInfo = PrintServiceInfo.create(
|
||||||
|
resolveInfo, getActivity());
|
||||||
|
String addPrintersActivity = printServiceInfo.getAddPrintersActivityName();
|
||||||
|
|
||||||
|
// No add printers activity declared - done.
|
||||||
|
if (TextUtils.isEmpty(addPrintersActivity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentName addPrintersComponentName = new ComponentName(
|
||||||
|
resolveInfo.serviceInfo.packageName,
|
||||||
|
addPrintersActivity);
|
||||||
|
Intent addPritnersIntent = new Intent(Intent.ACTION_MAIN)
|
||||||
|
.setComponent(addPrintersComponentName);
|
||||||
|
|
||||||
|
// The add printers activity is valid - add it.
|
||||||
|
if (!getActivity().getPackageManager().queryIntentActivities(
|
||||||
|
addPritnersIntent, 0).isEmpty()) {
|
||||||
|
mAddPrinterServices.add(printServiceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAddPrinterSelectionDialog() {
|
||||||
|
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||||
|
Fragment oldFragment = getFragmentManager().findFragmentByTag(
|
||||||
|
FRAGMRNT_TAG_ADD_PRINTER_DIALOG);
|
||||||
|
if (oldFragment != null) {
|
||||||
|
transaction.remove(oldFragment);
|
||||||
|
}
|
||||||
|
AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
|
||||||
|
Bundle arguments = new Bundle();
|
||||||
|
arguments.putParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS,
|
||||||
|
mAddPrinterServices);
|
||||||
|
newFragment.setArguments(arguments);
|
||||||
|
transaction.add(newFragment, FRAGMRNT_TAG_ADD_PRINTER_DIALOG);
|
||||||
|
transaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AddPrinterAlertDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
private static final String DEFAULT_MARKET_QUERY_STRING =
|
||||||
|
"market://search?q=print";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle(R.string.choose_print_service);
|
||||||
|
|
||||||
|
final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
|
||||||
|
getArguments().getParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS);
|
||||||
|
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
|
||||||
|
android.R.layout.simple_list_item_1);
|
||||||
|
final int printServiceCount = printServices.size();
|
||||||
|
for (int i = 0; i < printServiceCount; i++) {
|
||||||
|
PrintServiceInfo printService = printServices.get(i);
|
||||||
|
adapter.add(printService.getResolveInfo().loadLabel(
|
||||||
|
getActivity().getPackageManager()).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
PrintServiceInfo printService = printServices.get(which);
|
||||||
|
ComponentName componentName = new ComponentName(
|
||||||
|
printService.getResolveInfo().serviceInfo.packageName,
|
||||||
|
printService.getAddPrintersActivityName());
|
||||||
|
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||||
|
intent.setComponent(componentName);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Uri marketUri = Uri.parse(DEFAULT_MARKET_QUERY_STRING);
|
||||||
|
final Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
|
||||||
|
if (getActivity().getPackageManager().resolveActivity(marketIntent, 0) != null) {
|
||||||
|
builder.setPositiveButton(R.string.search_play_store,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
|
startActivity(marketIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DestinationAdapter extends BaseAdapter
|
||||||
|
implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>, Filterable {
|
||||||
|
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
|
private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
|
||||||
|
|
||||||
|
private final List<PrinterInfo> mFilteredPrinters = new ArrayList<PrinterInfo>();
|
||||||
|
|
||||||
|
private CharSequence mLastSearchString;
|
||||||
|
|
||||||
|
public DestinationAdapter() {
|
||||||
|
getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Filter getFilter() {
|
||||||
|
return new Filter() {
|
||||||
|
@Override
|
||||||
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (TextUtils.isEmpty(constraint)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
FilterResults results = new FilterResults();
|
||||||
|
List<PrinterInfo> filteredPrinters = new ArrayList<PrinterInfo>();
|
||||||
|
String constraintLowerCase = constraint.toString().toLowerCase();
|
||||||
|
final int printerCount = mPrinters.size();
|
||||||
|
for (int i = 0; i < printerCount; i++) {
|
||||||
|
PrinterInfo printer = mPrinters.get(i);
|
||||||
|
if (printer.getName().toLowerCase().contains(constraintLowerCase)) {
|
||||||
|
filteredPrinters.add(printer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results.values = filteredPrinters;
|
||||||
|
results.count = filteredPrinters.size();
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mLastSearchString = constraint;
|
||||||
|
mFilteredPrinters.clear();
|
||||||
|
if (results == null) {
|
||||||
|
mFilteredPrinters.addAll(mPrinters);
|
||||||
|
} else {
|
||||||
|
List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
|
||||||
|
mFilteredPrinters.addAll(printers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
synchronized (mLock) {
|
||||||
|
return mFilteredPrinters.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
return mFilteredPrinters.get(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getDropDownView(int position, View convertView,
|
||||||
|
ViewGroup parent) {
|
||||||
|
return getView(position, convertView, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = getActivity().getLayoutInflater().inflate(
|
||||||
|
R.layout.spinner_dropdown_item, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence title = null;
|
||||||
|
CharSequence subtitle = null;
|
||||||
|
|
||||||
|
PrinterInfo printer = (PrinterInfo) getItem(position);
|
||||||
|
title = printer.getName();
|
||||||
|
try {
|
||||||
|
PackageManager pm = getActivity().getPackageManager();
|
||||||
|
PackageInfo packageInfo = pm.getPackageInfo(printer.getId()
|
||||||
|
.getServiceName().getPackageName(), 0);
|
||||||
|
subtitle = packageInfo.applicationInfo.loadLabel(pm);
|
||||||
|
} catch (NameNotFoundException nnfe) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView titleView = (TextView) convertView.findViewById(R.id.title);
|
||||||
|
titleView.setText(title);
|
||||||
|
|
||||||
|
TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
|
||||||
|
if (!TextUtils.isEmpty(subtitle)) {
|
||||||
|
subtitleView.setText(subtitle);
|
||||||
|
subtitleView.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
subtitleView.setText(null);
|
||||||
|
subtitleView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
|
||||||
|
if (id == LOADER_ID_PRINTERS_LOADER) {
|
||||||
|
return new FusedPrintersProvider(getActivity());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(Loader<List<PrinterInfo>> loader,
|
||||||
|
List<PrinterInfo> printers) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mPrinters.clear();
|
||||||
|
mPrinters.addAll(printers);
|
||||||
|
mFilteredPrinters.clear();
|
||||||
|
mFilteredPrinters.addAll(printers);
|
||||||
|
if (!TextUtils.isEmpty(mLastSearchString)) {
|
||||||
|
getFilter().filter(mLastSearchString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mPrinters.clear();
|
||||||
|
mFilteredPrinters.clear();
|
||||||
|
}
|
||||||
|
notifyDataSetInvalidated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,8 +30,6 @@ import android.os.Message;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.print.IPrinterDiscoverySessionController;
|
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrintJobInfo;
|
import android.print.PrintJobInfo;
|
||||||
import android.print.PrintManager;
|
import android.print.PrintManager;
|
||||||
import android.print.PrinterId;
|
import android.print.PrinterId;
|
||||||
@@ -79,6 +77,10 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
|
|
||||||
private boolean mDestroyed;
|
private boolean mDestroyed;
|
||||||
|
|
||||||
|
private boolean mAllPrintJobsHandled;
|
||||||
|
|
||||||
|
private boolean mHasPrinterDiscoverySession;
|
||||||
|
|
||||||
public RemotePrintService(Context context, ComponentName componentName, int userId,
|
public RemotePrintService(Context context, ComponentName componentName, int userId,
|
||||||
RemotePrintSpooler spooler) {
|
RemotePrintSpooler spooler) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
@@ -97,6 +99,8 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
private void handleDestroy() {
|
private void handleDestroy() {
|
||||||
throwIfDestroyed();
|
throwIfDestroyed();
|
||||||
ensureUnbound();
|
ensureUnbound();
|
||||||
|
mAllPrintJobsHandled = false;
|
||||||
|
mHasPrinterDiscoverySession = false;
|
||||||
mDestroyed = true;
|
mDestroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,18 +114,27 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleBinderDied() {
|
private void handleBinderDied() {
|
||||||
|
mAllPrintJobsHandled = false;
|
||||||
|
mHasPrinterDiscoverySession = false;
|
||||||
mPendingCommands.clear();
|
mPendingCommands.clear();
|
||||||
ensureUnbound();
|
ensureUnbound();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleOnAllPrintJobsHandled() {
|
private void handleOnAllPrintJobsHandled() {
|
||||||
throwIfDestroyed();
|
throwIfDestroyed();
|
||||||
|
|
||||||
|
mAllPrintJobsHandled = true;
|
||||||
|
|
||||||
if (isBound()) {
|
if (isBound()) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled()");
|
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled()");
|
||||||
}
|
}
|
||||||
// If bound and all the work is completed, then unbind.
|
|
||||||
ensureUnbound();
|
// If the service has a printer discovery session
|
||||||
|
// created we should not disconnect from it just yet.
|
||||||
|
if (!mHasPrinterDiscoverySession) {
|
||||||
|
ensureUnbound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +166,9 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
|
|
||||||
private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
|
private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
|
||||||
throwIfDestroyed();
|
throwIfDestroyed();
|
||||||
|
|
||||||
|
mAllPrintJobsHandled = false;
|
||||||
|
|
||||||
if (!isBound()) {
|
if (!isBound()) {
|
||||||
ensureBound();
|
ensureBound();
|
||||||
mPendingCommands.add(new Runnable() {
|
mPendingCommands.add(new Runnable() {
|
||||||
@@ -173,20 +189,18 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
|
public void createPrinterDiscoverySession() {
|
||||||
mHandler.obtainMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
|
mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
|
||||||
observer).sendToTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCreatePrinterDiscoverySession(
|
private void handleCreatePrinterDiscoverySession() {
|
||||||
final IPrinterDiscoverySessionObserver observer) {
|
|
||||||
throwIfDestroyed();
|
throwIfDestroyed();
|
||||||
if (!isBound()) {
|
if (!isBound()) {
|
||||||
ensureBound();
|
ensureBound();
|
||||||
mPendingCommands.add(new Runnable() {
|
mPendingCommands.add(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
handleCreatePrinterDiscoverySession(observer);
|
handleCreatePrinterDiscoverySession();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -194,9 +208,126 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()");
|
Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
mPrintService.createPrinterDiscoverySession(observer);
|
mPrintService.createPrinterDiscoverySession();
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
Slog.e(LOG_TAG, "Error announcing start printer dicovery.", re);
|
Slog.e(LOG_TAG, "Error creating printer dicovery session.", re);
|
||||||
|
}
|
||||||
|
|
||||||
|
mHasPrinterDiscoverySession = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroyPrinterDiscoverySession() {
|
||||||
|
mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDestroyPrinterDiscoverySession() {
|
||||||
|
throwIfDestroyed();
|
||||||
|
if (!isBound()) {
|
||||||
|
ensureBound();
|
||||||
|
mPendingCommands.add(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleDestroyPrinterDiscoverySession();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserId + "] destroyPrinterDiscoverySession()");
|
||||||
|
}
|
||||||
|
|
||||||
|
mHasPrinterDiscoverySession = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mPrintService.destroyPrinterDiscoverySession();
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error destroying printer dicovery session.", re);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the service has no print jobs and no active discovery
|
||||||
|
// session anymore we should disconnect from it.
|
||||||
|
if (mAllPrintJobsHandled) {
|
||||||
|
ensureUnbound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startPrinterDiscovery(List<PrinterId> priorityList) {
|
||||||
|
mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
|
||||||
|
priorityList).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) {
|
||||||
|
throwIfDestroyed();
|
||||||
|
if (!isBound()) {
|
||||||
|
ensureBound();
|
||||||
|
mPendingCommands.add(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleStartPrinterDiscovery(priorityList);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterDiscovery()");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mPrintService.startPrinterDiscovery(priorityList);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error starting printer dicovery.", re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopPrinterDiscovery() {
|
||||||
|
mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStopPrinterDiscovery() {
|
||||||
|
throwIfDestroyed();
|
||||||
|
if (!isBound()) {
|
||||||
|
ensureBound();
|
||||||
|
mPendingCommands.add(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleStopPrinterDiscovery();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterDiscovery()");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mPrintService.stopPrinterDiscovery();
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error stopping printer dicovery.", re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestPrinterUpdate(PrinterId printerId) {
|
||||||
|
mHandler.obtainMessage(MyHandler.MSG_REQUEST_PRINTER_UPDATE,
|
||||||
|
printerId).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRequestPrinterUpdate(final PrinterId printerId) {
|
||||||
|
throwIfDestroyed();
|
||||||
|
if (!isBound()) {
|
||||||
|
ensureBound();
|
||||||
|
mPendingCommands.add(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleRequestPrinterUpdate(printerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserId + "] requestPrinterUpdate()");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mPrintService.requestPrinterUpdate(printerId);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error requesting a printer update.", re);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,20 +410,47 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class MyHandler extends Handler {
|
private final class MyHandler extends Handler {
|
||||||
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 1;
|
public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
|
||||||
public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 2;
|
public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
|
||||||
public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
|
public static final int MSG_START_PRINTER_DISCOVERY = 3;
|
||||||
public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 4;
|
public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
|
||||||
public static final int MSG_DESTROY = 6;
|
public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
|
||||||
public static final int MSG_BINDER_DIED = 7;
|
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 6;
|
||||||
|
public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 7;
|
||||||
|
public static final int MSG_ON_PRINT_JOB_QUEUED = 8;
|
||||||
|
public static final int MSG_DESTROY = 9;
|
||||||
|
public static final int MSG_BINDER_DIED = 10;
|
||||||
|
|
||||||
public MyHandler(Looper looper) {
|
public MyHandler(Looper looper) {
|
||||||
super(looper, null, false);
|
super(looper, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
|
case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
|
||||||
|
handleCreatePrinterDiscoverySession();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
|
||||||
|
handleDestroyPrinterDiscoverySession();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_START_PRINTER_DISCOVERY: {
|
||||||
|
List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj;
|
||||||
|
handleStartPrinterDiscovery(priorityList);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_STOP_PRINTER_DISCOVERY: {
|
||||||
|
handleStopPrinterDiscovery();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MSG_REQUEST_PRINTER_UPDATE: {
|
||||||
|
PrinterId printerId = (PrinterId) message.obj;
|
||||||
|
handleRequestPrinterUpdate(printerId);
|
||||||
|
} break;
|
||||||
|
|
||||||
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
|
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
|
||||||
handleOnAllPrintJobsHandled();
|
handleOnAllPrintJobsHandled();
|
||||||
} break;
|
} break;
|
||||||
@@ -307,13 +465,6 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
handleOnPrintJobQueued(printJob);
|
handleOnPrintJobQueued(printJob);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
|
|
||||||
IPrinterDiscoverySessionObserver observer =
|
|
||||||
(IPrinterDiscoverySessionObserver) message.obj;
|
|
||||||
handleCreatePrinterDiscoverySession(new SecurePrinterDiscoverySessionObserver(
|
|
||||||
mComponentName, observer));
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MSG_DESTROY: {
|
case MSG_DESTROY: {
|
||||||
handleDestroy();
|
handleDestroy();
|
||||||
} break;
|
} break;
|
||||||
@@ -363,7 +514,7 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setPrintJobState(int printJobId, int state, CharSequence error) {
|
public boolean setPrintJobState(int printJobId, int state, String error) {
|
||||||
RemotePrintService service = mWeakService.get();
|
RemotePrintService service = mWeakService.get();
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
@@ -402,79 +553,70 @@ final class RemotePrintService implements DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static final class SecurePrinterDiscoverySessionObserver
|
|
||||||
extends IPrinterDiscoverySessionObserver.Stub {
|
|
||||||
private final ComponentName mComponentName;
|
|
||||||
|
|
||||||
private final IPrinterDiscoverySessionObserver mDecoratedObsever;
|
|
||||||
|
|
||||||
public SecurePrinterDiscoverySessionObserver(ComponentName componentName,
|
|
||||||
IPrinterDiscoverySessionObserver observer) {
|
|
||||||
mComponentName = componentName;
|
|
||||||
mDecoratedObsever = observer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrintersAdded(List<PrinterInfo> printers) {
|
public void onPrintersAdded(List<PrinterInfo> printers) {
|
||||||
throwIfPrinterIdsForPrinterInfoTampered(printers);
|
RemotePrintService service = mWeakService.get();
|
||||||
try {
|
if (service != null) {
|
||||||
mDecoratedObsever.onPrintersAdded(printers);
|
throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers);
|
||||||
} catch (RemoteException re) {
|
final long identity = Binder.clearCallingIdentity();
|
||||||
Slog.e(LOG_TAG, "Error delegating to onPrintersAdded", re);
|
try {
|
||||||
}
|
service.mSpooler.onPrintersAdded(printers);
|
||||||
}
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
@Override
|
}
|
||||||
public void onPrintersUpdated(List<PrinterInfo> printers) {
|
|
||||||
throwIfPrinterIdsForPrinterInfoTampered(printers);
|
|
||||||
try {
|
|
||||||
mDecoratedObsever.onPrintersUpdated(printers);
|
|
||||||
} catch (RemoteException re) {
|
|
||||||
Slog.e(LOG_TAG, "Error delegating to onPrintersUpdated.", re);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrintersRemoved(List<PrinterId> printerIds) {
|
public void onPrintersRemoved(List<PrinterId> printerIds) {
|
||||||
throwIfPrinterIdsTampered(printerIds);
|
RemotePrintService service = mWeakService.get();
|
||||||
try {
|
if (service != null) {
|
||||||
mDecoratedObsever.onPrintersRemoved(printerIds);
|
throwIfPrinterIdsTampered(service.mComponentName, printerIds);
|
||||||
} catch (RemoteException re) {
|
final long identity = Binder.clearCallingIdentity();
|
||||||
Slog.e(LOG_TAG, "Error delegating to onPrintersRemoved", re);
|
try {
|
||||||
|
service.mSpooler.onPrintersRemoved(printerIds);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setController(IPrinterDiscoverySessionController controller) {
|
public void onPrintersUpdated(List<PrinterInfo> printers) {
|
||||||
try {
|
RemotePrintService service = mWeakService.get();
|
||||||
mDecoratedObsever.setController(controller);
|
if (service != null) {
|
||||||
} catch (RemoteException re) {
|
throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers);
|
||||||
Slog.e(LOG_TAG, "Error setting controller", re);
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
service.mSpooler.onPrintersUpdated(printers);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwIfPrinterIdsForPrinterInfoTampered(
|
private void throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName,
|
||||||
List<PrinterInfo> printerInfos) {
|
List<PrinterInfo> printerInfos) {
|
||||||
final int printerInfoCount = printerInfos.size();
|
final int printerInfoCount = printerInfos.size();
|
||||||
for (int i = 0; i < printerInfoCount; i++) {
|
for (int i = 0; i < printerInfoCount; i++) {
|
||||||
PrinterId printerId = printerInfos.get(i).getId();
|
PrinterId printerId = printerInfos.get(i).getId();
|
||||||
throwIfPrinterIdTampered(printerId);
|
throwIfPrinterIdTampered(serviceName, printerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwIfPrinterIdsTampered(List<PrinterId> printerIds) {
|
private void throwIfPrinterIdsTampered(ComponentName serviceName,
|
||||||
|
List<PrinterId> printerIds) {
|
||||||
final int printerIdCount = printerIds.size();
|
final int printerIdCount = printerIds.size();
|
||||||
for (int i = 0; i < printerIdCount; i++) {
|
for (int i = 0; i < printerIdCount; i++) {
|
||||||
PrinterId printerId = printerIds.get(i);
|
PrinterId printerId = printerIds.get(i);
|
||||||
throwIfPrinterIdTampered(printerId);
|
throwIfPrinterIdTampered(serviceName, printerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwIfPrinterIdTampered(PrinterId printerId) {
|
private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) {
|
||||||
if (printerId == null || printerId.getServiceName() == null
|
if (printerId == null || printerId.getServiceName() == null
|
||||||
|| !printerId.getServiceName().equals(mComponentName)) {
|
|| !printerId.getServiceName().equals(serviceName)) {
|
||||||
throw new IllegalArgumentException("Invalid printer id: " + printerId);
|
throw new IllegalArgumentException("Invalid printer id: " + printerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ import android.print.IPrintDocumentAdapter;
|
|||||||
import android.print.IPrintSpooler;
|
import android.print.IPrintSpooler;
|
||||||
import android.print.IPrintSpoolerCallbacks;
|
import android.print.IPrintSpoolerCallbacks;
|
||||||
import android.print.IPrintSpoolerClient;
|
import android.print.IPrintSpoolerClient;
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrintAttributes;
|
import android.print.PrintAttributes;
|
||||||
import android.print.PrintJobInfo;
|
import android.print.PrintJobInfo;
|
||||||
|
import android.print.PrinterId;
|
||||||
|
import android.print.PrinterInfo;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.TimedRemoteCaller;
|
import android.util.TimedRemoteCaller;
|
||||||
|
|
||||||
@@ -92,7 +93,11 @@ final class RemotePrintSpooler {
|
|||||||
public static interface PrintSpoolerCallbacks {
|
public static interface PrintSpoolerCallbacks {
|
||||||
public void onPrintJobQueued(PrintJobInfo printJob);
|
public void onPrintJobQueued(PrintJobInfo printJob);
|
||||||
public void onAllPrintJobsForServiceHandled(ComponentName printService);
|
public void onAllPrintJobsForServiceHandled(ComponentName printService);
|
||||||
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
|
public void createPrinterDiscoverySession();
|
||||||
|
public void destroyPrinterDiscoverySession();
|
||||||
|
public void startPrinterDiscovery(List<PrinterId> priorityList);
|
||||||
|
public void stopPrinterDiscovery();
|
||||||
|
public void requestPrinterUpdate(PrinterId printerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemotePrintSpooler(Context context, int userId,
|
public RemotePrintSpooler(Context context, int userId,
|
||||||
@@ -209,7 +214,7 @@ final class RemotePrintSpooler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean setPrintJobState(int printJobId, int state, CharSequence error) {
|
public final boolean setPrintJobState(int printJobId, int state, String error) {
|
||||||
throwIfCalledOnMainThread();
|
throwIfCalledOnMainThread();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
throwIfDestroyedLocked();
|
throwIfDestroyedLocked();
|
||||||
@@ -300,6 +305,78 @@ final class RemotePrintSpooler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void onPrintersAdded(List<PrinterInfo> printers) {
|
||||||
|
throwIfCalledOnMainThread();
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
mCanUnbind = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
getRemoteInstanceLazy().onPrintersAdded(printers);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error adding printers.", re);
|
||||||
|
} catch (TimeoutException te) {
|
||||||
|
Slog.e(LOG_TAG, "Error adding printers.", te);
|
||||||
|
} finally {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
|
||||||
|
+ "] onPrintersAdded()");
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCanUnbind = true;
|
||||||
|
mLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void onPrintersRemoved(List<PrinterId> printerIds) {
|
||||||
|
throwIfCalledOnMainThread();
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
mCanUnbind = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
getRemoteInstanceLazy().onPrintersRemoved(printerIds);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error removing printers.", re);
|
||||||
|
} catch (TimeoutException te) {
|
||||||
|
Slog.e(LOG_TAG, "Error removing printers.", te);
|
||||||
|
} finally {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
|
||||||
|
+ "] onPrintersRemoved()");
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCanUnbind = true;
|
||||||
|
mLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void onPrintersUpdated(List<PrinterInfo> printers) {
|
||||||
|
throwIfCalledOnMainThread();
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
mCanUnbind = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
getRemoteInstanceLazy().onPrintersUpdated(printers);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Slog.e(LOG_TAG, "Error updating printers.", re);
|
||||||
|
} catch (TimeoutException te) {
|
||||||
|
Slog.e(LOG_TAG, "Error updating printers.", te);
|
||||||
|
} finally {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
|
||||||
|
+ "] onPrintersUpdted()");
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCanUnbind = true;
|
||||||
|
mLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
|
private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mRemoteInstance != null) {
|
if (mRemoteInstance != null) {
|
||||||
@@ -488,7 +565,7 @@ final class RemotePrintSpooler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean setPrintJobState(IPrintSpooler target, int printJobId,
|
public boolean setPrintJobState(IPrintSpooler target, int printJobId,
|
||||||
int status, CharSequence error) throws RemoteException, TimeoutException {
|
int status, String error) throws RemoteException, TimeoutException {
|
||||||
final int sequence = onBeforeRemoteCall();
|
final int sequence = onBeforeRemoteCall();
|
||||||
target.setPrintJobState(printJobId, status, error, mCallback, sequence);
|
target.setPrintJobState(printJobId, status, error, mCallback, sequence);
|
||||||
return getResultTimed(sequence);
|
return getResultTimed(sequence);
|
||||||
@@ -597,12 +674,64 @@ final class RemotePrintSpooler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
|
public void createPrinterDiscoverySession() {
|
||||||
RemotePrintSpooler spooler = mWeakSpooler.get();
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
||||||
if (spooler != null) {
|
if (spooler != null) {
|
||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
spooler.mCallbacks.createPrinterDiscoverySession(observer);
|
spooler.mCallbacks.createPrinterDiscoverySession();
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyPrinterDiscoverySession() {
|
||||||
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
||||||
|
if (spooler != null) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
spooler.mCallbacks.destroyPrinterDiscoverySession();
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startPrinterDiscovery(List<PrinterId> priorityList) {
|
||||||
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
||||||
|
if (spooler != null) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
spooler.mCallbacks.startPrinterDiscovery(priorityList);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopPrinterDiscovery() {
|
||||||
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
||||||
|
if (spooler != null) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
spooler.mCallbacks.stopPrinterDiscovery();
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestPrinterUpdate(PrinterId printerId) {
|
||||||
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
||||||
|
if (spooler != null) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
spooler.mCallbacks.requestPrinterUpdate(printerId);
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(identity);
|
Binder.restoreCallingIdentity(identity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.print.IPrinterDiscoverySessionObserver;
|
|
||||||
import android.print.PrintJobInfo;
|
import android.print.PrintJobInfo;
|
||||||
|
import android.print.PrinterId;
|
||||||
import android.printservice.PrintServiceInfo;
|
import android.printservice.PrintServiceInfo;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -105,7 +105,7 @@ final class UserState implements PrintSpoolerCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
|
public void createPrinterDiscoverySession() {
|
||||||
final List<RemotePrintService> services;
|
final List<RemotePrintService> services;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
throwIfDestroyedLocked();
|
throwIfDestroyedLocked();
|
||||||
@@ -117,7 +117,73 @@ final class UserState implements PrintSpoolerCallbacks {
|
|||||||
final int serviceCount = services.size();
|
final int serviceCount = services.size();
|
||||||
for (int i = 0; i < serviceCount; i++) {
|
for (int i = 0; i < serviceCount; i++) {
|
||||||
RemotePrintService service = services.get(i);
|
RemotePrintService service = services.get(i);
|
||||||
service.createPrinterDiscoverySession(observer);
|
service.createPrinterDiscoverySession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyPrinterDiscoverySession() {
|
||||||
|
final List<RemotePrintService> services;
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
if (mActiveServices.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
services = new ArrayList<RemotePrintService>(mActiveServices.values());
|
||||||
|
}
|
||||||
|
final int serviceCount = services.size();
|
||||||
|
for (int i = 0; i < serviceCount; i++) {
|
||||||
|
RemotePrintService service = services.get(i);
|
||||||
|
service.destroyPrinterDiscoverySession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startPrinterDiscovery(List<PrinterId> printerIds) {
|
||||||
|
final List<RemotePrintService> services;
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
if (mActiveServices.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
services = new ArrayList<RemotePrintService>(mActiveServices.values());
|
||||||
|
}
|
||||||
|
final int serviceCount = services.size();
|
||||||
|
for (int i = 0; i < serviceCount; i++) {
|
||||||
|
RemotePrintService service = services.get(i);
|
||||||
|
service.startPrinterDiscovery(printerIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopPrinterDiscovery() {
|
||||||
|
final List<RemotePrintService> services;
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
if (mActiveServices.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
services = new ArrayList<RemotePrintService>(mActiveServices.values());
|
||||||
|
}
|
||||||
|
final int serviceCount = services.size();
|
||||||
|
for (int i = 0; i < serviceCount; i++) {
|
||||||
|
RemotePrintService service = services.get(i);
|
||||||
|
service.stopPrinterDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestPrinterUpdate(PrinterId printerId) {
|
||||||
|
final RemotePrintService service;
|
||||||
|
synchronized (mLock) {
|
||||||
|
throwIfDestroyedLocked();
|
||||||
|
if (mActiveServices.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
service = mActiveServices.get(printerId.getServiceName());
|
||||||
|
}
|
||||||
|
if (service != null) {
|
||||||
|
service.requestPrinterUpdate(printerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user