From 269403b032f965ff3847eb982c2f697229dc5a92 Mon Sep 17 00:00:00 2001
From: Svetoslav
Date: Wed, 14 Aug 2013 17:31:04 -0700
Subject: [PATCH] Implemented advanced printer selection and API refactoring.
1. Added past printer history tracking and merging favorite printers
with discovered printers.
2. Added save as PDF support.
3. Added all printers activity with search capability and optional
add printers chooser (if any print service provides add printers
activity)
4. Refactored the printer discovery session APIs. Now one session
can have multiple window discovery windows and the session stores
the printers found during past discovery periods.
5. Merged the print spooler and the print spooler service - much
simpler and easier to maintain.
Change-Id: I4830b0eb6367e1c748b768a5ea9ea11baf36cfad
---
Android.mk | 2 -
api/current.txt | 14 +-
core/java/android/print/IPrintSpooler.aidl | 8 +-
.../android/print/IPrintSpoolerClient.aidl | 9 +-
.../IPrinterDiscoverySessionController.aidl | 30 -
.../IPrinterDiscoverySessionObserver.aidl | 33 -
core/java/android/print/PrintJobInfo.java | 27 +-
core/java/android/print/PrintManager.java | 6 +-
core/java/android/print/PrinterInfo.java | 7 +-
core/java/android/print/pdf/PdfDocument.java | 2 +-
.../android/printservice/IPrintService.aidl | 9 +-
.../printservice/IPrintServiceClient.aidl | 6 +-
core/java/android/printservice/PrintJob.java | 21 +-
.../android/printservice/PrintService.java | 124 +-
.../printservice/PrinterDiscoverySession.java | 574 +++++---
.../android/internal/os/HandlerCaller.java | 6 +-
packages/PrintSpooler/AndroidManifest.xml | 9 +-
.../res/drawable-hdpi/ic_menu_add.png | Bin 0 -> 667 bytes
.../res/drawable-mdpi/ic_menu_add.png | Bin 0 -> 596 bytes
.../res/drawable-xhdpi/ic_menu_add.png | Bin 0 -> 761 bytes
.../print_job_config_activity_container.xml | 5 -
...tivity.xml => select_printer_activity.xml} | 18 +-
.../res/layout/spinner_dropdown_item.xml | 2 +-
...tivity.xml => select_printer_activity.xml} | 10 +-
packages/PrintSpooler/res/values/strings.xml | 23 +-
packages/PrintSpooler/res/values/themes.xml | 8 +
.../AvailablePrinterProvider.java | 285 ----
.../com/android/printspooler/DataLoader.java | 33 -
.../android/printspooler/DataProvider.java | 50 -
.../printspooler/FavoritePrinterProvider.java | 364 -----
.../printspooler/FusedPrintersProvider.java | 575 ++++++++
.../printspooler/PrintJobConfigActivity.java | 770 +++++++---
.../android/printspooler/PrintSpooler.java | 969 -------------
.../printspooler/PrintSpoolerService.java | 1258 ++++++++++++++++-
...tivity.java => SelectPrinterActivity.java} | 19 +-
.../printspooler/SelectPrinterFragment.java | 401 ++++++
.../server/print/RemotePrintService.java | 282 +++-
.../server/print/RemotePrintSpooler.java | 141 +-
.../com/android/server/print/UserState.java | 72 +-
39 files changed, 3794 insertions(+), 2378 deletions(-)
delete mode 100644 core/java/android/print/IPrinterDiscoverySessionController.aidl
delete mode 100644 core/java/android/print/IPrinterDiscoverySessionObserver.aidl
create mode 100644 packages/PrintSpooler/res/drawable-hdpi/ic_menu_add.png
create mode 100644 packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png
create mode 100644 packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png
rename packages/PrintSpooler/res/layout/{choose_printer_activity.xml => select_printer_activity.xml} (58%)
rename packages/PrintSpooler/res/menu/{choose_printer_activity.xml => select_printer_activity.xml} (79%)
delete mode 100644 packages/PrintSpooler/src/com/android/printspooler/AvailablePrinterProvider.java
delete mode 100644 packages/PrintSpooler/src/com/android/printspooler/DataLoader.java
delete mode 100644 packages/PrintSpooler/src/com/android/printspooler/DataProvider.java
delete mode 100644 packages/PrintSpooler/src/com/android/printspooler/FavoritePrinterProvider.java
create mode 100644 packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
delete mode 100644 packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
rename packages/PrintSpooler/src/com/android/printspooler/{ChoosePrinterActivity.java => SelectPrinterActivity.java} (53%)
create mode 100644 packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
diff --git a/Android.mk b/Android.mk
index 656e40c2cba26..ff6d4a004629d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -162,8 +162,6 @@ LOCAL_SRC_FILES += \
core/java/android/os/IVibratorService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
core/java/android/print/ILayoutResultCallback.aidl \
- core/java/android/print/IPrinterDiscoverySessionController.aidl \
- core/java/android/print/IPrinterDiscoverySessionObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrintClient.aidl \
core/java/android/print/IPrintManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 4fb2fb9e51f87..82ffaac5bb614 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19031,7 +19031,7 @@ package android.print {
method public android.print.PrintAttributes getAttributes();
method public int getCopies();
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.PrinterId getPrinterId();
method public int getState();
@@ -19155,7 +19155,7 @@ package android.printservice {
public final class PrintJob {
method public boolean cancel();
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 int getId();
method public android.print.PrintJobInfo getInfo();
@@ -19184,11 +19184,15 @@ package android.printservice {
}
public abstract class PrinterDiscoverySession {
- ctor public PrinterDiscoverySession(android.content.Context);
+ ctor public PrinterDiscoverySession();
method public final void addPrinters(java.util.List);
- method public abstract void onClose();
- method public abstract void onOpen(java.util.List);
+ method public final java.util.List getPrinters();
+ 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 onStartPrinterDiscovery(java.util.List);
+ method public abstract void onStopPrinterDiscovery();
method public final void removePrinters(java.util.List);
method public final void updatePrinters(java.util.List);
}
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 81781801d0db9..5c8a22ae5de04 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -18,6 +18,7 @@ package android.print;
import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
+import android.print.PrinterId;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
import android.print.IPrintSpoolerClient;
@@ -40,10 +41,15 @@ oneway interface IPrintSpooler {
void createPrintJob(String printJobName, in IPrintClient client,
in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
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);
void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback,
int sequence);
void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
void setClient(IPrintSpoolerClient client);
+
+ // Printer discovery APIs
+ void onPrintersAdded(in List printers);
+ void onPrintersRemoved(in List printerIds);
+ void onPrintersUpdated(in List printerIds);
}
diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl
index 8db2169635aff..da6012029c686 100644
--- a/core/java/android/print/IPrintSpoolerClient.aidl
+++ b/core/java/android/print/IPrintSpoolerClient.aidl
@@ -17,7 +17,6 @@
package android.print;
import android.content.ComponentName;
-import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrinterId;
import android.print.PrintJobInfo;
@@ -28,8 +27,14 @@ import android.print.PrintJobInfo;
* @hide
*/
oneway interface IPrintSpoolerClient {
- void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
void onPrintJobQueued(in PrintJobInfo printJob);
void onAllPrintJobsForServiceHandled(in ComponentName printService);
void onAllPrintJobsHandled();
+
+ // Printer discovery APIs
+ void createPrinterDiscoverySession();
+ void startPrinterDiscovery(in List priorityList);
+ void stopPrinterDiscovery();
+ void requestPrinterUpdate(in PrinterId printerId);
+ void destroyPrinterDiscoverySession();
}
diff --git a/core/java/android/print/IPrinterDiscoverySessionController.aidl b/core/java/android/print/IPrinterDiscoverySessionController.aidl
deleted file mode 100644
index 13116ef1ccb65..0000000000000
--- a/core/java/android/print/IPrinterDiscoverySessionController.aidl
+++ /dev/null
@@ -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 priorityList);
- void requestPrinterUpdate(in PrinterId printerId);
- void close();
-}
diff --git a/core/java/android/print/IPrinterDiscoverySessionObserver.aidl b/core/java/android/print/IPrinterDiscoverySessionObserver.aidl
deleted file mode 100644
index a78924c6171bc..0000000000000
--- a/core/java/android/print/IPrinterDiscoverySessionObserver.aidl
+++ /dev/null
@@ -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 printers);
- void onPrintersRemoved(in List printerIds);
- void onPrintersUpdated(in List printerIds);
-}
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 2fb4751af76fb..602f3c1f2a44b 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -104,7 +104,7 @@ public final class PrintJobInfo implements Parcelable {
private int mId;
/** The human readable print job label. */
- private CharSequence mLabel;
+ private String mLabel;
/** The unique id of the printer. */
private PrinterId mPrinterId;
@@ -128,7 +128,7 @@ public final class PrintJobInfo implements Parcelable {
private int mCopies;
/** Failure reason if this job failed. */
- private CharSequence mFailureReason;
+ private String mFailureReason;
/** The pages to print */
private PageRange[] mPageRanges;
@@ -163,7 +163,7 @@ public final class PrintJobInfo implements Parcelable {
private PrintJobInfo(Parcel parcel) {
mId = parcel.readInt();
- mLabel = parcel.readCharSequence();
+ mLabel = parcel.readString();
mPrinterId = parcel.readParcelable(null);
mPrinterName = parcel.readString();
mState = parcel.readInt();
@@ -171,9 +171,7 @@ public final class PrintJobInfo implements Parcelable {
mUserId = parcel.readInt();
mTag = parcel.readString();
mCopies = parcel.readInt();
- if (parcel.readInt() == 1) {
- mFailureReason = parcel.readCharSequence();
- }
+ mFailureReason = parcel.readString();
if (parcel.readInt() == 1) {
Parcelable[] parcelables = parcel.readParcelableArray(null);
mPageRanges = new PageRange[parcelables.length];
@@ -214,7 +212,7 @@ public final class PrintJobInfo implements Parcelable {
*
* @return The label.
*/
- public CharSequence getLabel() {
+ public String getLabel() {
return mLabel;
}
@@ -225,7 +223,7 @@ public final class PrintJobInfo implements Parcelable {
*
* @hide
*/
- public void setLabel(CharSequence label) {
+ public void setLabel(String label) {
mLabel = label;
}
@@ -385,7 +383,7 @@ public final class PrintJobInfo implements Parcelable {
*
* @hide
*/
- public CharSequence getFailureReason() {
+ public String getFailureReason() {
return mFailureReason;
}
@@ -396,7 +394,7 @@ public final class PrintJobInfo implements Parcelable {
*
* @hide
*/
- public void setFailureReason(CharSequence failureReason) {
+ public void setFailureReason(String failureReason) {
mFailureReason = failureReason;
}
@@ -470,7 +468,7 @@ public final class PrintJobInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mId);
- parcel.writeCharSequence(mLabel);
+ parcel.writeString(mLabel);
parcel.writeParcelable(mPrinterId, flags);
parcel.writeString(mPrinterName);
parcel.writeInt(mState);
@@ -478,12 +476,7 @@ public final class PrintJobInfo implements Parcelable {
parcel.writeInt(mUserId);
parcel.writeString(mTag);
parcel.writeInt(mCopies);
- if (mFailureReason != null) {
- parcel.writeInt(1);
- parcel.writeCharSequence(mFailureReason);
- } else {
- parcel.writeInt(0);
- }
+ parcel.writeString(mFailureReason);
if (mPageRanges != null) {
parcel.writeInt(1);
parcel.writeParcelableArray(mPageRanges, flags);
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 636b9d4cb77be..531dcb224a3c7 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -374,14 +374,14 @@ public final class PrintManager {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ if (info == null) {
+ throw new NullPointerException("document info cannot be null");
+ }
final ILayoutResultCallback callback;
synchronized (mLock) {
callback = mCallback;
clearLocked();
}
- if (info == null) {
- throw new IllegalArgumentException("info cannot be null");
- }
if (callback != null) {
try {
callback.onLayoutFinished(info, changed, mSequence);
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index ac782a83927fc..6f567a64d709e 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -229,10 +229,11 @@ public final class PrinterInfo implements Parcelable {
/**
* Constructor.
*
- * @param prototype Prototype from which to start building.
+ * @param other Other info from which to start building.
*/
- public Builder(PrinterInfo prototype) {
- mPrototype = prototype;
+ public Builder(PrinterInfo other) {
+ mPrototype = new PrinterInfo();
+ mPrototype.copyFrom(other);
}
/**
diff --git a/core/java/android/print/pdf/PdfDocument.java b/core/java/android/print/pdf/PdfDocument.java
index cfeb97543d956..dbd7dd1448c54 100644
--- a/core/java/android/print/pdf/PdfDocument.java
+++ b/core/java/android/print/pdf/PdfDocument.java
@@ -324,7 +324,7 @@ public final class PdfDocument {
/**
* Creates a new builder with the mandatory page info attributes.
*
- * @param pageSize The page size in pixels.
+ * @param pageSize The page size in points, not dips.
* @param pageNumber The page number.
* @param density The page density in DPI.
*/
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl
index 16b7a26868ef4..2cee1d84d0fbf 100644
--- a/core/java/android/printservice/IPrintService.aidl
+++ b/core/java/android/printservice/IPrintService.aidl
@@ -16,7 +16,7 @@
package android.printservice;
-import android.print.IPrinterDiscoverySessionObserver;
+import android.print.PrinterId;
import android.print.PrintJobInfo;
import android.printservice.IPrintServiceClient;
@@ -29,5 +29,10 @@ oneway interface IPrintService {
void setClient(IPrintServiceClient client);
void requestCancelPrintJob(in PrintJobInfo printJobInfo);
void onPrintJobQueued(in PrintJobInfo printJobInfo);
- void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
+
+ void createPrinterDiscoverySession();
+ void startPrinterDiscovery(in List priorityList);
+ void stopPrinterDiscovery();
+ void requestPrinterUpdate(in PrinterId printerId);
+ void destroyPrinterDiscoverySession();
}
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index f00b37c4bf0f1..1e33fc07c92cf 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -29,7 +29,11 @@ import android.print.PrinterInfo;
interface IPrintServiceClient {
List getPrintJobInfos();
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);
oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
+
+ void onPrintersAdded(in List printers);
+ void onPrintersRemoved(in List printerIds);
+ void onPrintersUpdated(in List printers);
}
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 5f9a730f3f4a3..d2fbef2b578e5 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -24,6 +24,10 @@ import android.util.Log;
* This class represents a print job from the perspective of a print
* service. It provides APIs for observing the print job state and
* performing operations on the print job.
+ *
+ * Note: All methods of this class must be executed on the main
+ * application thread.
+ *
*/
public final class PrintJob {
@@ -48,6 +52,7 @@ public final class PrintJob {
* @return The id.
*/
public int getId() {
+ PrintService.throwIfNotCalledOnMainThread();
return mCachedInfo.getId();
}
@@ -62,6 +67,7 @@ public final class PrintJob {
* @return The print job info.
*/
public PrintJobInfo getInfo() {
+ PrintService.throwIfNotCalledOnMainThread();
if (isInImmutableState()) {
return mCachedInfo;
}
@@ -83,6 +89,7 @@ public final class PrintJob {
* @return The document.
*/
public PrintDocument getDocument() {
+ PrintService.throwIfNotCalledOnMainThread();
return mDocument;
}
@@ -96,6 +103,7 @@ public final class PrintJob {
* @see #cancel()
*/
public boolean isQueued() {
+ PrintService.throwIfNotCalledOnMainThread();
return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
}
@@ -110,6 +118,7 @@ public final class PrintJob {
* @see #fail(CharSequence)
*/
public boolean isStarted() {
+ PrintService.throwIfNotCalledOnMainThread();
return getInfo().getState() == PrintJobInfo.STATE_STARTED;
}
@@ -122,6 +131,7 @@ public final class PrintJob {
* @see #complete()
*/
public boolean isCompleted() {
+ PrintService.throwIfNotCalledOnMainThread();
return getInfo().getState() == PrintJobInfo.STATE_COMPLETED;
}
@@ -134,6 +144,7 @@ public final class PrintJob {
* @see #fail(CharSequence)
*/
public boolean isFailed() {
+ PrintService.throwIfNotCalledOnMainThread();
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
}
@@ -146,6 +157,7 @@ public final class PrintJob {
* @see #cancel()
*/
public boolean isCancelled() {
+ PrintService.throwIfNotCalledOnMainThread();
return getInfo().getState() == PrintJobInfo.STATE_FAILED;
}
@@ -158,6 +170,7 @@ public final class PrintJob {
* @see #isQueued()
*/
public boolean start() {
+ PrintService.throwIfNotCalledOnMainThread();
if (isQueued()) {
return setState(PrintJobInfo.STATE_STARTED, null);
}
@@ -173,6 +186,7 @@ public final class PrintJob {
* @see #isStarted()
*/
public boolean complete() {
+ PrintService.throwIfNotCalledOnMainThread();
if (isStarted()) {
return setState(PrintJobInfo.STATE_COMPLETED, null);
}
@@ -191,7 +205,8 @@ public final class PrintJob {
* @see #isQueued()
* @see #isStarted()
*/
- public boolean fail(CharSequence error) {
+ public boolean fail(String error) {
+ PrintService.throwIfNotCalledOnMainThread();
if (isQueued() || isStarted()) {
return setState(PrintJobInfo.STATE_FAILED, error);
}
@@ -210,6 +225,7 @@ public final class PrintJob {
* @see #isQueued()
*/
public boolean cancel() {
+ PrintService.throwIfNotCalledOnMainThread();
if (isQueued() || isStarted()) {
return setState(PrintJobInfo.STATE_CANCELED, null);
}
@@ -226,6 +242,7 @@ public final class PrintJob {
* @return True if the tag was set, false otherwise.
*/
public boolean setTag(String tag) {
+ PrintService.throwIfNotCalledOnMainThread();
if (isInImmutableState()) {
return false;
}
@@ -263,7 +280,7 @@ public final class PrintJob {
|| state == PrintJobInfo.STATE_CANCELED;
}
- private boolean setState(int state, CharSequence error) {
+ private boolean setState(int state, String error) {
try {
if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) {
// Best effort - update the state of the cached info since
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 92bccd4e480d7..8fe770c3be5fe 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -25,7 +25,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.util.Log;
@@ -146,6 +145,11 @@ import java.util.List;
* {@link #SERVICE_META_DATA} and <{@link android.R.styleable#PrintService
* print-service}>.
*
+ *
+ * Note: 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.
+ *
*/
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";
- private final Object mLock = new Object();
-
private Handler mHandler;
private IPrintServiceClient mClient;
private int mLastSessionId = -1;
+ private PrinterDiscoverySession mDiscoverySession;
+
@Override
protected final void attachBaseContext(Context base) {
super.attachBaseContext(base);
@@ -245,21 +249,18 @@ public abstract class PrintService extends Service {
* @see PrintJob#isStarted() PrintJob.isStarted()
*/
public final List getActivePrintJobs() {
- final IPrintServiceClient client;
- synchronized (mLock) {
- client = mClient;
- }
- if (client == null) {
+ throwIfNotCalledOnMainThread();
+ if (mClient == null) {
return Collections.emptyList();
}
try {
List printJobs = null;
- List printJobInfos = client.getPrintJobInfos();
+ List printJobInfos = mClient.getPrintJobInfos();
if (printJobInfos != null) {
final int printJobInfoCount = printJobInfos.size();
printJobs = new ArrayList(printJobInfoCount);
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) {
@@ -278,23 +279,50 @@ public abstract class PrintService extends Service {
* @return Global printer id.
*/
public final PrinterId generatePrinterId(String localId) {
+ throwIfNotCalledOnMainThread();
return new PrinterId(new ComponentName(getPackageName(),
getClass().getName()), localId);
}
+ static void throwIfNotCalledOnMainThread() {
+ if (!Looper.getMainLooper().isCurrentThread()) {
+ throw new IllegalAccessError("must be called from the main thread");
+ }
+ }
+
@Override
public final IBinder onBind(Intent intent) {
return new IPrintService.Stub() {
@Override
- public void setClient(IPrintServiceClient client) {
- mHandler.obtainMessage(ServiceHandler.MSG_SET_CLEINT, client)
- .sendToTarget();
+ public void createPrinterDiscoverySession() {
+ mHandler.sendEmptyMessage(ServiceHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
}
@Override
- public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
- mHandler.obtainMessage(ServiceHandler.MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION,
- observer).sendToTarget();
+ public void destroyPrinterDiscoverySession() {
+ mHandler.sendEmptyMessage(ServiceHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
+ }
+
+ public void startPrinterDiscovery(List 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
@@ -312,33 +340,62 @@ public abstract class PrintService extends Service {
}
private final class ServiceHandler extends Handler {
- public static final int MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION = 1;
- public static final int MSG_ON_PRINTJOB_QUEUED = 2;
- public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 3;
- public static final int MSG_SET_CLEINT = 4;
+ public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
+ public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
+ public static final int MSG_START_PRINTER_DISCOVERY = 3;
+ 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) {
super(looper, null, true);
}
@Override
+ @SuppressWarnings("unchecked")
public void handleMessage(Message message) {
final int action = message.what;
switch (action) {
- case MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION: {
- IPrinterDiscoverySessionObserver observer =
- (IPrinterDiscoverySessionObserver) message.obj;
+ case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
PrinterDiscoverySession session = onCreatePrinterDiscoverySession();
if (session == null) {
throw new NullPointerException("session cannot be null");
}
- synchronized (mLock) {
- if (session.getId() == mLastSessionId) {
- throw new IllegalStateException("cannot reuse sessions");
- }
- mLastSessionId = session.getId();
+ if (session.getId() == mLastSessionId) {
+ throw new IllegalStateException("cannot reuse session instances");
+ }
+ mDiscoverySession = session;
+ 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 priorityList = (ArrayList) 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;
case MSG_ON_REQUEST_CANCEL_PRINTJOB: {
@@ -352,15 +409,12 @@ public abstract class PrintService extends Service {
} break;
case MSG_SET_CLEINT: {
- IPrintServiceClient client = (IPrintServiceClient) message.obj;
- synchronized (mLock) {
- mClient = client;
- }
- if (client != null) {
+ mClient = (IPrintServiceClient) message.obj;
+ if (mClient != null) {
onConnected();
} else {
onDisconnected();
- }
+ }
} break;
default: {
diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java
index 92dc0dd4a05fd..8b959a68690a6 100644
--- a/core/java/android/printservice/PrinterDiscoverySession.java
+++ b/core/java/android/printservice/PrinterDiscoverySession.java
@@ -16,18 +16,15 @@
package android.printservice;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
-import android.print.IPrinterDiscoverySessionController;
-import android.print.IPrinterDiscoverySessionObserver;
+import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
+import android.util.ArrayMap;
import android.util.Log;
-import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -36,67 +33,75 @@ import java.util.List;
* for adding discovered printers, removing already added printers that
* disappeared, and updating already added printers.
*
- * The opening of the session is announced by a call to {@link
- * PrinterDiscoverySession#onOpen(List)} at which point you should start printer
- * discovery. The closing of the session is announced by a call to {@link
- * PrinterDiscoverySession#onClose()} at which point you should stop printer
- * discovery. Discovered printers are added by invoking {@link
- * PrinterDiscoverySession#addPrinters(List)}. Added printers that disappeared
- * are removed by invoking {@link PrinterDiscoverySession#removePrinters(List)}.
- * Added printers whose properties or capabilities changed are updated through
- * a call to {@link PrinterDiscoverySession#updatePrinters(List)}.
+ * During the lifetime of this session you may be asked to start and stop
+ * performing printer discovery multiple times. You will receive a call to {@link
+ * PrinterDiscoverySession#onStartPrinterDiscovery(List)} to start printer
+ * discovery and a call to {@link PrinterDiscoverySession#onStopPrinterDiscovery()}
+ * to stop printer discovery. When the system is no longer interested in printers
+ * discovered by this session you will receive a call to {@link #onDestroy()} at
+ * which point the system will no longer call into the session and all the session
+ * methods will do nothing.
+ *
+ *
+ * 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 not persisted across sessions.
*
*
* The system will make a call to
* {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
* need to update a given printer. It is possible that you add a printer without
- * specifying its capabilities. This enables you to avoid querying all
- * discovered printers for their capabilities, rather querying the capabilities
- * of a printer only if necessary. For example, the system will require that you
- * update a printer if it gets selected by the user. If you did not report the
- * printer capabilities when adding it, you must do so after the system requests
- * a printer update. Otherwise, the printer will be ignored.
+ * specifying its capabilities. This enables you to avoid querying all discovered
+ * printers for their capabilities, rather querying the capabilities of a printer
+ * only if necessary. For example, the system will request that you update a printer
+ * if it gets selected by the user. If you did not report the printer capabilities
+ * when adding it, you must do so after the system requests a printer update.
+ * Otherwise, the printer will be ignored.
*
*
- * During printer discovery all printers that are known to your print service
- * have to be added. The system does not retain any printers from previous
- * sessions.
+ * Note: All callbacks in this class are executed on the main
+ * application thread. You also have to invoke any method of this class on the main
+ * application thread.
*
*/
public abstract class PrinterDiscoverySession {
private static final String LOG_TAG = "PrinterDiscoverySession";
+ private static final int MAX_ITEMS_PER_CALLBACK = 100;
+
private static int sIdCounter = 0;
- private final Object mLock = new Object();
-
- private final Handler mHandler;
-
private final int mId;
- private IPrinterDiscoverySessionController mController;
+ private final ArrayMap mPrinters =
+ new ArrayMap();
- private IPrinterDiscoverySessionObserver mObserver;
+ private ArrayMap mLastSentPrinters;
+
+ private IPrintServiceClient mObserver;
+
+ private boolean mIsDestroyed;
+
+ private boolean mIsDiscoveryStarted;
/**
* Constructor.
- *
- * @param context A context instance.
*/
- public PrinterDiscoverySession(Context context) {
+ public PrinterDiscoverySession() {
mId = sIdCounter++;
- mHandler = new SessionHandler(context.getMainLooper());
- mController = new PrinterDiscoverySessionController(this);
}
- void setObserver(IPrinterDiscoverySessionObserver observer) {
- synchronized (mLock) {
- mObserver = observer;
- try {
- mObserver.setController(mController);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error setting session controller", re);
- }
+ void setObserver(IPrintServiceClient observer) {
+ mObserver = observer;
+ // If some printers were added in the method that
+ // created the session, send them over.
+ if (!mPrinters.isEmpty()) {
+ sendAddedPrinters(mObserver, getPrinters());
}
}
@@ -104,132 +109,358 @@ public abstract class PrinterDiscoverySession {
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.
+ *
+ * Note: Calls to this method after the session is
+ * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
+ *
+ *
+ * @return The printers.
+ *
+ * @see #addPrinters(List)
+ * @see #removePrinters(List)
+ * @see #updatePrinters(List)
+ * @see #isDestroyed()
+ */
+ public final List getPrinters() {
+ PrintService.throwIfNotCalledOnMainThread();
+ if (mIsDestroyed) {
+ return Collections.emptyList();
+ }
+ return new ArrayList(mPrinters.values());
+ }
+
/**
* Adds discovered printers. Adding an already added printer has no effect.
* Removed printers can be added again. You can call this method multiple
- * times during printer discovery.
+ * times during the life of this session. Duplicates will be ignored.
*
- * Note: Calls to this method before the session is opened,
- * i.e. before the {@link #onOpen(List)} call, and after the session is closed,
- * i.e. after the call to {@link #onClose()}, will be ignored.
+ * Note: Calls to this method after the session is
+ * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
*
*
* @param printers The printers to add.
*
* @see #removePrinters(List)
* @see #updatePrinters(List)
+ * @see #getPrinters()
+ * @see #isDestroyed()
*/
public final void addPrinters(List printers) {
- final IPrinterDiscoverySessionObserver observer;
- synchronized (mLock) {
- observer = mObserver;
+ PrintService.throwIfNotCalledOnMainThread();
+
+ // If the session is destroyed - nothing do to.
+ if (mIsDestroyed) {
+ Log.w(LOG_TAG, "Not adding printers - session destroyed.");
+ return;
}
- if (observer != null) {
- try {
- observer.onPrintersAdded(printers);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error adding printers", re);
+
+ if (mIsDiscoveryStarted) {
+ // If during discovery, add the new printers and send them.
+ List addedPrinters = new ArrayList();
+ 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 {
- 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(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 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 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
- * printer has no effect. Removed printers can be added again. You
- * can call this method multiple times during printer discovery.
+ * printer has no effect. Removed printers can be added again. You can
+ * call this method multiple times during the lifetime of this session.
*
- * Note: Calls to this method before the session is opened,
- * i.e. before the {@link #onOpen(List)} call, and after the session is closed,
- * i.e. after the call to {@link #onClose()}, will be ignored.
+ * Note: Calls to this method after the session is
+ * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
*
*
* @param printerIds The ids of the removed printers.
*
* @see #addPrinters(List)
* @see #updatePrinters(List)
+ * @see #getPrinters()
+ * @see #isDestroyed()
*/
public final void removePrinters(List printerIds) {
- final IPrinterDiscoverySessionObserver observer;
- synchronized (mLock) {
- observer = mObserver;
+ PrintService.throwIfNotCalledOnMainThread();
+
+ // If the session is destroyed - nothing do to.
+ if (mIsDestroyed) {
+ Log.w(LOG_TAG, "Not removing printers - session destroyed.");
+ return;
}
- if (observer != null) {
- try {
- observer.onPrintersRemoved(printerIds);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error removing printers", re);
+
+ if (mIsDiscoveryStarted) {
+ // If during discovery, remove existing printers and send them.
+ List removedPrinterIds = new ArrayList();
+ 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 {
- 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(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 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 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
* was removed has no effect. You can call this method multiple times
- * during printer discovery.
+ * during the lifetime of this session.
*
- * Note: Calls to this method before the session is opened,
- * i.e. before the {@link #onOpen(List)} call, and after the session is closed,
- * i.e. after the call to {@link #onClose()}, will be ignored.
+ * Note: Calls to this method after the session is
+ * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
*
*
* @param printers The printers to update.
*
* @see #addPrinters(List)
* @see #removePrinters(List)
+ * @see #getPrinters()
+ * @see #isDestroyed()
*/
public final void updatePrinters(List printers) {
- final IPrinterDiscoverySessionObserver observer;
- synchronized (mLock) {
- observer = mObserver;
+ PrintService.throwIfNotCalledOnMainThread();
+
+ // If the session is destroyed - nothing do to.
+ if (mIsDestroyed) {
+ Log.w(LOG_TAG, "Not updating printers - session destroyed.");
+ return;
}
- if (observer != null) {
- try {
- observer.onPrintersUpdated(printers);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error updating printers", re);
+
+ if (mIsDiscoveryStarted) {
+ // If during discovery, update existing printers and send them.
+ List updatedPrinters = new ArrayList();
+ 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 {
- 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(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 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 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 addedPrinters = null;
+ List updatedPrinters = null;
+ List 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();
+ }
+ updatedPrinters.add(printer);
+ }
+ } else {
+ if (addedPrinters == null) {
+ addedPrinters = new ArrayList();
+ }
+ 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();
+ }
+ 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
- * printer discovery. Discovered printers should be added via calling
- * {@link #addPrinters(List)}. Added printers that disappeared should be
- * removed via calling {@link #removePrinters(List)}. Added printers whose
- * properties or capabilities changes should be updated via calling {@link
- * #updatePrinters(List)}. When the session is closed you will receive a
- * call to {@link #onClose()}.
+ * Callback asking you to start printer discovery. Discovered printers should be
+ * added via calling {@link #addPrinters(List)}. Added printers that disappeared
+ * should be removed via calling {@link #removePrinters(List)}. Added printers
+ * whose properties or capabilities changed should be updated via calling {@link
+ * #updatePrinters(List)}. You will receive a call to call to {@link
+ * #onStopPrinterDiscovery()} when you should stop printer discovery.
*
- * During printer discovery all printers that are known to your print
- * service have to be added. The system does not retain any printers from
- * previous sessions.
+ * 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 across 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.
*
*
- * Note: You are also given a list of printers whose
- * availability has to be checked first. For example, these printers could
- * be the user's favorite ones, therefore they have to be verified first.
+ * Note: You are also given a list of printers whose availability
+ * has to be checked first. For example, these printers could be the user's favorite
+ * ones, therefore they have to be verified first.
*
*
- * @see #onClose()
+ * @param priorityList The list of printers to validate first. Never null.
+ *
+ * @see #onStopPrinterDiscovery()
* @see #addPrinters(List)
* @see #removePrinters(List)
* @see #updatePrinters(List)
+ * @see #isPrinterDiscoveryStarted()
*/
- public abstract void onOpen(List priorityList);
+ public abstract void onStartPrinterDiscovery(List priorityList);
/**
- * Callback notifying you that the session is closed and you should stop
- * printer discovery. After the session is closed any call to the methods
- * of this instance will be ignored. Once the session is closed
- * it will never be opened again.
+ * Callback notifying you that you should stop printer discovery.
+ *
+ * @see #onStartPrinterDiscovery(List)
+ * @see #isPrinterDiscoveryStarted()
*/
- public abstract void onClose();
+ public abstract void onStopPrinterDiscovery();
/**
* 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);
- void close() {
- synchronized (mLock) {
- mController = null;
+ /**
+ * Notifies you that the session is destroyed. After this callback is invoked
+ * 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 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;
+ 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 priorityList = (List) 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 mWeakSession;
-
- public PrinterDiscoverySessionController(PrinterDiscoverySession session) {
- mWeakSession = new WeakReference(session);
- }
-
- @Override
- public void open(List priorityList) {
- PrinterDiscoverySession session = mWeakSession.get();
- if (session != null) {
- session.mHandler.obtainMessage(SessionHandler.MSG_OPEN,
- priorityList).sendToTarget();
- }
- }
-
- @Override
- public void close() {
- PrinterDiscoverySession session = mWeakSession.get();
- if (session != null) {
- session.mHandler.sendEmptyMessage(SessionHandler.MSG_CLOSE);
- }
- }
-
- @Override
- public void requestPrinterUpdate(PrinterId printerId) {
- PrinterDiscoverySession session = mWeakSession.get();
- if (session != null) {
- session.mHandler.obtainMessage(
- SessionHandler.MSG_REQUEST_PRINTER_UPDATE,
- printerId).sendToTarget();
- }
- }
- };
}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index b442ff52f6351..d9e3ef661cce9 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -65,7 +65,11 @@ public class HandlerCaller {
mH.sendMessage(msg);
}
-
+
+ public void sendMessageDelayed(Message msg, long delayMillis) {
+ mH.sendMessageDelayed(msg, delayMillis);
+ }
+
public boolean hasMessages(int what) {
return mH.hasMessages(what);
}
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index c00639d57cdf8..1f10af82e8767 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -18,7 +18,7 @@
-->
@@ -51,9 +51,10 @@
+ android:name=".SelectPrinterActivity"
+ android:label="@string/all_printers_label"
+ android:theme="@style/SelectPrinterActivityTheme"
+ android:exported="false">
NS%G}U;vjb?
zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0
z-G$*l2rk&Wd@@jkv%n*=n1O*?2!t6g-L3lrG(N-A#W5t~-rJcQ{SF%lwAn9DJlR*l
zyKIH?&58-@YMJF4*^^hykf>x$aCf*Y?EkT&
zWg1G0BQqIaY<&>(MenrQQLYf4&9!_wTYj^W&c%a&(v
z0yU8=9F7~S|DCq&NUGj5vnDw5hW>!C8<`)
zMX5lF!N|bSNY}tr*U&t~(9Fuj!pZ>1HMcS_2;zJXiaCUa-29Zxv`X9>ltaSxff{5$
xHU#IVm6RtIr7}3C^c$_r?GK
literal 0
HcmV?d00001
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png b/packages/PrintSpooler/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000000000000000000000000000000000000..15ffadd3606f09de78cae03693ad50c2335a2e04
GIT binary patch
literal 596
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb?
zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCC7XMsm#F#`j)5C}6~x?A@LXuP7Qi(^Q{;kVNRy^a_NwCyj{)_bFO
z?{qo8vR!xk$55N2acO}Y9!;y4pH$Mm=ZP@s3{
zVo~U;1V)2uhFn3bHtDRTN0_f|WQ=IntQR^s(=eK$BsW2k(_qE{*9OtNMZ)ObhNZ
zA8AnDP&zUH@gJ}K%MUccRoyo*$1Qp*SD`MGc}+0HvoPrU(kpk{GFg5w*hv;j>*ZQh3hFVau?PHey&|$s
zpDBT*%J>`$9~nlofYW%-xOKF@$5s9NG0QIe8al4_M)lnSI6j0_Bo
zbPY^(4b4Lg&8$o;tPFr$b1MUbAkO!oAV+A(%}>cptHiBAIV4;ks6hr~LvVgtNqJ&X
oDuZK6ep0G}TV_#ed45rLv4V53Y2nt#Wzopr03f~8Gynhq
literal 0
HcmV?d00001
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000000000000000000000000000000000000..420510e935cf5ae4ecf824e681f44af9102a03e3
GIT binary patch
literal 761
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Ea{HEjtmSN`?>!lvI6;x#X;^)
z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPV
z+ueoXKL{?^yL>WGgtNdSvY3H^TL^?1FWs&C12q1ar;B4q#jUrqefn9}k^$PT4nCSNroHqm8#cdHNn(oU1T&
zy&%AJpk(=O6Y02fK8X%u+{%?$n5`wj-KW8CakGqbPrLW6yy^a8O3$9My{10J)jU|mtje1+-C4#q6TtUC-_
z8BFsUq8esh`~Ogp<&2J)0sF%Kh(f+x*6>ia9HHM;>Pv-;mukg5+he;t=ejbl1^Ygu
zV+N~|vTvHcP*7}HIdiIFi}ZuWRe94lms~YbOqo~17{l~E?VHH+qKt%7Eold2ew@g0
zFn*fM_QWJ%-lFT}jV*?(ANbY?9bdlbH^&;IIO{CEU2JmE%#Xw_U03?S5p(6O`GaY4
zVh5{av+uG6zsvCa`sewQl-n0(Fy5c}xMWcjvs!2E-pjKX4O4$rq~873y?*`m$H&)M
z|B5|;&wEnl4!RD#pS&+N-Jcbk{`TE|$%K&7-$(uKXYfQYm*>>$?GNJ!oF{Qp+<23)
zasN!4Kg4bg7ec#$`gxH85~pclTsDj
gGK*5n^NX^J6`X@j3%5Qt1FB>2boFyt=akR{00+z=&Hw-a
literal 0
HcmV?d00001
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
index a0c111b3aa532..78170947915c7 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
@@ -20,9 +20,4 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/container_background">
-
-
-
-
diff --git a/packages/PrintSpooler/res/layout/choose_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
similarity index 58%
rename from packages/PrintSpooler/res/layout/choose_printer_activity.xml
rename to packages/PrintSpooler/res/layout/select_printer_activity.xml
index c34a108f5024b..f4e18537c7a73 100644
--- a/packages/PrintSpooler/res/layout/choose_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -5,7 +5,7 @@
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
+ 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,
@@ -14,10 +14,16 @@
limitations under the License.
-->
-
-
+ android:layout_height="wrap_content">
+
+
+
+
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
index 002cc1458345e..d14c0642af94a 100644
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
@@ -15,7 +15,7 @@
-->
+ android:imeOptions="actionSearch">
+
+
+ -
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 1cd611f0c9202..41fc5168791f2 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -58,11 +58,32 @@
Generating print job
-
+
+ Save as PDF
+
+
+ All printers\.\.\.
+
+
+ Searching for printers\.\.\.
+
+
Search
+
+ All printers
+
+
+
+
+ Choose print service
+
+
+ Search in play store
+
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index ab16c65eca995..831b0ecb2a47d 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -24,4 +24,12 @@
- @android:color/transparent
+
+
+
+
diff --git a/packages/PrintSpooler/src/com/android/printspooler/AvailablePrinterProvider.java b/packages/PrintSpooler/src/com/android/printspooler/AvailablePrinterProvider.java
deleted file mode 100644
index 658a22417418f..0000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/AvailablePrinterProvider.java
+++ /dev/null
@@ -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
- implements DataLoader {
- private static final String LOG_TAG = "AvailablePrinterProvider";
-
- private final Set mPrinteIdsSet = new ArraySet();
-
- private final List mPrinters = new ArrayList();
-
- private final List mPriorityList;
-
- private PrinterDiscoverySession mDiscoverySession;
-
- public AvailablePrinterProvider(Context context, List 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 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 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 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 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 printers = (List) message.obj;
- addPrinters(printers);
- } break;
-
- case MSG_ON_PRINTERS_REMOVED: {
- List printers = (List) message.obj;
- removePrinters(printers);
- } break;
-
- case MSG_ON_PRINTERS_UPDATED: {
- List printers = (List) message.obj;
- updatePrinters(printers);
- } break;
- };
- }
- }
- }
-
- private static final class PrinterDiscoverySessionObserver
- extends IPrinterDiscoverySessionObserver.Stub {
-
- private final WeakReference mWeakSession;
-
- public PrinterDiscoverySessionObserver(PrinterDiscoverySession session) {
- mWeakSession = new WeakReference(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 printers) {
- PrinterDiscoverySession sesison = mWeakSession.get();
- if (sesison != null) {
- sesison.mHandler.obtainMessage(
- PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_ADDED,
- printers).sendToTarget();
- }
- }
-
- @Override
- public void onPrintersRemoved(List printers) {
- PrinterDiscoverySession session = mWeakSession.get();
- if (session != null) {
- session.mHandler.obtainMessage(
- PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_REMOVED,
- printers).sendToTarget();
- }
- }
-
- @Override
- public void onPrintersUpdated(List printers) {
- PrinterDiscoverySession session = mWeakSession.get();
- if (session != null) {
- session.mHandler.obtainMessage(
- PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_UPDATED,
- printers).sendToTarget();
- }
- }
- };
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/DataLoader.java b/packages/PrintSpooler/src/com/android/printspooler/DataLoader.java
deleted file mode 100644
index 82cc2e10d5f2b..0000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/DataLoader.java
+++ /dev/null
@@ -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();
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/DataProvider.java b/packages/PrintSpooler/src/com/android/printspooler/DataProvider.java
deleted file mode 100644
index 7b10903f55701..0000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/DataProvider.java
+++ /dev/null
@@ -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 The type of the providers data.
- */
-public abstract class DataProvider 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);
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FavoritePrinterProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FavoritePrinterProvider.java
deleted file mode 100644
index 2c539d192611d..0000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/FavoritePrinterProvider.java
+++ /dev/null
@@ -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 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 mHistoricalPrinters = new ArrayList();
-
- private final List mFavoritePrinters = new ArrayList();
-
- 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 recordMap =
- new ArrayMap();
-
- // 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 {
- 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, Void, Void>() {
- @Override
- protected Void doInBackground(List... printers) {
- doWriteState(printers[0]);
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- notifyChanged();
- }
-
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
- new ArrayList(mHistoricalPrinters));
- }
-
- private void doWriteState(List 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;
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
new file mode 100644
index 0000000000000..6bad5b3ffb82d
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -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> {
+ 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 mPrinters =
+ new LinkedHashMap();
+
+ private final PersistenceManager mPersistenceManager;
+
+ private PrinterDiscoverySession mDiscoverySession;
+
+ private List mFavoritePrinters;
+
+ public FusedPrintersProvider(Context context) {
+ super(context);
+ mPersistenceManager = new PersistenceManager(context);
+ }
+
+ public void addHistoricalPrinter(PrinterInfo printer) {
+ mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
+ }
+
+ public List getPrinters() {
+ return new ArrayList(mPrinters.values());
+ }
+
+ @Override
+ public void deliverResult(List 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(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 printerIds = new ArrayList(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 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(mPrinters.values()));
+ }
+ }
+
+ @Override
+ public void onPrintersRemoved(List 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(mPrinters.values()));
+ }
+ }
+
+ @Override
+ public void onPrintersUpdated(List 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(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 mHistoricalPrinters;
+
+ private boolean mReadHistoryCompleted;
+ private boolean mReadHistoryInProgress;
+
+ private final AsyncTask> mReadTask =
+ new AsyncTask>() {
+ @Override
+ protected List doInBackground(Void... args) {
+ return doReadPrinterHistory();
+ }
+
+ @Override
+ protected void onPostExecute(List 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 doReadPrinterHistory() {
+ FileInputStream in = null;
+ try {
+ in = mStatePersistFile.openRead();
+ } catch (FileNotFoundException fnfe) {
+ Log.i(LOG_TAG, "No existing printer history.");
+ return new ArrayList();
+ }
+ try {
+ List printers = new ArrayList();
+ 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 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 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, Void, Void> mWriteTask =
+ new AsyncTask, Void, Void>() {
+ @Override
+ protected Void doInBackground(List... printers) {
+ doWritePrinterHistory(printers[0]);
+ return null;
+ }
+
+ private void doWritePrinterHistory(List 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 computeFavoritePrinters(List printers) {
+ Map recordMap =
+ new ArrayMap();
+
+ // 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 favoriteRecords = new ArrayList(
+ recordMap.values());
+ Collections.sort(favoriteRecords);
+
+ // Write the favorites to the output.
+ final int favoriteCount = favoriteRecords.size();
+ List favoritePrinters = new ArrayList(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 {
+ 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);
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index f8e9f43eda3f1..d3dd8c973dbd7 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -18,13 +18,17 @@ package com.android.printspooler;
import android.app.Activity;
import android.app.Dialog;
+import android.app.LoaderManager;
import android.content.Context;
+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.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -39,9 +43,11 @@ import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
+import android.print.PrintManager;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -69,6 +75,14 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -93,15 +107,32 @@ public class PrintJobConfigActivity extends Activity {
public static final String EXTRA_PRINT_ATTRIBUTES = "printAttributes";
public static final String EXTRA_PRINT_JOB_ID = "printJobId";
- private static final int CONTROLLER_STATE_INITIALIZED = 1;
- private static final int CONTROLLER_STATE_STARTED = 2;
- private static final int CONTROLLER_STATE_LAYOUT_STARTED = 3;
- private static final int CONTROLLER_STATE_LAYOUT_COMPLETED = 4;
- private static final int CONTROLLER_STATE_WRITE_STARTED = 5;
- private static final int CONTROLLER_STATE_WRITE_COMPLETED = 6;
- private static final int CONTROLLER_STATE_FINISHED = 7;
- private static final int CONTROLLER_STATE_FAILED = 8;
- private static final int CONTROLLER_STATE_CANCELLED = 9;
+ public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
+
+ private static final int LOADER_ID_PRINTERS_LOADER = 1;
+
+ private static final int DEST_ADAPTER_MIN_ITEM_COUNT = 2;
+ private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
+
+ private static final int DEST_ADAPTER_POSITION_SEARCHING_FOR_PRINTERS = 0;
+ private static final int DEST_ADAPTER_POSITION_SAVE_AS_PDF = 1;
+
+ private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
+ private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
+ private static final int DEST_ADAPTER_ITEM_ID_SEARCHING_FOR_PRINTERS = Integer.MAX_VALUE - 2;
+
+ private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
+ private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
+
+ private static final int CONTROLLER_STATE_FINISHED = 1;
+ private static final int CONTROLLER_STATE_FAILED = 2;
+ private static final int CONTROLLER_STATE_CANCELLED = 3;
+ private static final int CONTROLLER_STATE_INITIALIZED = 4;
+ private static final int CONTROLLER_STATE_STARTED = 5;
+ private static final int CONTROLLER_STATE_LAYOUT_STARTED = 6;
+ private static final int CONTROLLER_STATE_LAYOUT_COMPLETED = 7;
+ private static final int CONTROLLER_STATE_WRITE_STARTED = 8;
+ private static final int CONTROLLER_STATE_WRITE_COMPLETED = 9;
private static final int EDITOR_STATE_INITIALIZED = 1;
private static final int EDITOR_STATE_CONFIRMED_PRINT = 2;
@@ -109,6 +140,7 @@ public class PrintJobConfigActivity extends Activity {
private static final int EDITOR_STATE_CANCELLED = 4;
private static final int MIN_COPIES = 1;
+ private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
private static final Pattern PATTERN_DIGITS = Pattern.compile("\\d");
@@ -135,10 +167,6 @@ public class PrintJobConfigActivity extends Activity {
private Document mDocument;
private PrintController mController;
- private AvailablePrinterProvider mAvailablePrinters;
-
- private FavoritePrinterProvider mFavoritePrinters;
-
private int mPrintJobId;
private IBinder mIPrintDocumentAdapter;
@@ -148,9 +176,6 @@ public class PrintJobConfigActivity extends Activity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
- setContentView(R.layout.print_job_config_activity_container);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
Bundle extras = getIntent().getExtras();
@@ -169,15 +194,17 @@ public class PrintJobConfigActivity extends Activity {
mCurrPrintAttributes.copyFrom(attributes);
}
- // TODO: Use history
- mAvailablePrinters = new AvailablePrinterProvider(this, null);
- mFavoritePrinters = new FavoritePrinterProvider(this);
+ setContentView(R.layout.print_job_config_activity_container);
+
+ // TODO: This should be on the style
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+
mEditor = new Editor();
mDocument = new Document();
mController = new PrintController(new RemotePrintDocumentAdapter(
IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
- PrintSpooler.peekInstance().generateFileForPrintJob(mPrintJobId)));
+ PrintSpoolerService.peekInstance().generateFileForPrintJob(mPrintJobId)));
try {
mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
@@ -190,26 +217,6 @@ public class PrintJobConfigActivity extends Activity {
mEditor.initialize();
}
- @Override
- protected void onResume() {
- super.onResume();
- // TODO: Polish this
- if (!mEditor.isPrintConfirmed()) {
- mAvailablePrinters.startLoadData();
- mFavoritePrinters.startLoadData();
- }
- }
-
- @Override
- protected void onPause() {
- // TODO: Polish this
- if (!mEditor.isPrintConfirmed()) {
- mAvailablePrinters.stopLoadData();
- mFavoritePrinters.stopLoadData();
- }
- super.onPause();
- }
-
@Override
protected void onDestroy() {
// We can safely do the work in here since at this point
@@ -219,10 +226,10 @@ public class PrintJobConfigActivity extends Activity {
mController.finish();
}
if (mEditor.isPrintConfirmed() && mController.isFinished()) {
- PrintSpooler.peekInstance().setPrintJobState(mPrintJobId,
+ PrintSpoolerService.peekInstance().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_QUEUED, null);
} else {
- PrintSpooler.peekInstance().setPrintJobState(mPrintJobId,
+ PrintSpoolerService.peekInstance().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_CANCELED, null);
}
mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
@@ -333,13 +340,13 @@ public class PrintJobConfigActivity extends Activity {
public void update() {
if (!printAttributesChanged()) {
- // If the attributes changes, then we do not do a layout but may
+ // If the attributes changed, then we do not do a layout but may
// have to ask the app to write some pages. Hence, pretend layout
// completed and nothing changed, so we handle writing as usual.
handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
} else {
- PrintSpooler.peekInstance().setPrintJobAttributesNoPersistence(mPrintJobId,
- mCurrPrintAttributes);
+ PrintSpoolerService.peekInstance().setPrintJobAttributesNoPersistence(
+ mPrintJobId, mCurrPrintAttributes);
mMetadata.putBoolean(PrintDocumentAdapter.METADATA_KEY_PRINT_PREVIEW,
!mEditor.isPrintConfirmed());
@@ -378,7 +385,7 @@ public class PrintJobConfigActivity extends Activity {
final boolean infoChanged = !info.equals(mDocument.info);
if (infoChanged) {
mDocument.info = info;
- PrintSpooler.peekInstance().setPrintJobPrintDocumentInfoNoPersistence(
+ PrintSpoolerService.peekInstance().setPrintJobPrintDocumentInfoNoPersistence(
mPrintJobId, info);
}
@@ -465,12 +472,12 @@ public class PrintJobConfigActivity extends Activity {
if (Arrays.equals(mDocument.pages, mRequestedPages)) {
// We got a document with exactly the pages we wanted. Hence,
// the printer has to print all pages in the data.
- PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
+ PrintSpoolerService.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
ALL_PAGES_ARRAY);
} else if (Arrays.equals(mDocument.pages, ALL_PAGES_ARRAY)) {
// We requested specific pages but got all of them. Hence,
// the printer has to print only the requested pages.
- PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
+ PrintSpoolerService.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
mRequestedPages);
} else if (PageRangeUtils.contains(mDocument.pages, mRequestedPages)) {
// We requested specific pages and got more but not all pages.
@@ -480,7 +487,7 @@ public class PrintJobConfigActivity extends Activity {
final int offset = mDocument.pages[0].getStart() - pages[0].getStart();
PageRange[] offsetPages = Arrays.copyOf(mDocument.pages, mDocument.pages.length);
PageRangeUtils.offsetStart(offsetPages, offset);
- PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
+ PrintSpoolerService.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
offsetPages);
} else if (Arrays.equals(mRequestedPages, ALL_PAGES_ARRAY)
&& mDocument.pages.length == 1 && mDocument.pages[0].getStart() == 0
@@ -488,7 +495,7 @@ public class PrintJobConfigActivity extends Activity {
// We requested all pages via the special constant and got all
// of them as an explicit enumeration. Hence, the printer has
// to print only the requested pages.
- PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
+ PrintSpoolerService.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
mDocument.pages);
} else {
// We did not get the pages we requested, then the application
@@ -500,7 +507,16 @@ public class PrintJobConfigActivity extends Activity {
}
if (mEditor.isDone()) {
- PrintJobConfigActivity.this.finish();
+ if (mEditor.isPrintingToPdf()) {
+ PrintJobInfo printJob = PrintSpoolerService.peekInstance()
+ .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.setType("application/pdf");
+ intent.putExtra(Intent.EXTRA_TITLE, printJob.getLabel());
+ startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
+ } else {
+ PrintJobConfigActivity.this.finish();
+ }
}
}
@@ -607,33 +623,111 @@ public class PrintJobConfigActivity extends Activity {
}
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case ACTIVITY_REQUEST_CREATE_FILE: {
+ if (data != null) {
+ Uri uri = data.getData();
+ writePrintJobDataAndFinish(uri);
+ } else {
+ mEditor.showUi(Editor.UI_EDITING_PRINT_JOB,
+ new Runnable() {
+ @Override
+ public void run() {
+ mEditor.initialize();
+ mEditor.bindUi();
+ mEditor.updateUi();
+ }
+ });
+ }
+ } break;
+
+ case ACTIVITY_REQUEST_SELECT_PRINTER: {
+ if (resultCode == RESULT_OK) {
+ PrinterId printerId = (PrinterId) data.getParcelableExtra(
+ INTENT_EXTRA_PRINTER_ID);
+ // TODO: Make sure the selected printer is in the shown list.
+ mEditor.selectPrinter(printerId);
+ }
+ } break;
+ }
+ }
+
+ private void writePrintJobDataAndFinish(final Uri uri) {
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ PrintJobInfo printJob = PrintSpoolerService.peekInstance()
+ .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
+ if (printJob == null) {
+ return null;
+ }
+ File file = PrintSpoolerService.peekInstance()
+ .generateFileForPrintJob(mPrintJobId);
+ in = new FileInputStream(file);
+ out = getContentResolver().openOutputStream(uri);
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ 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);
+ }
+ return null;
+ }
+
+ @Override
+ public void onPostExecute(Void result) {
+ mEditor.cancel();
+ PrintJobConfigActivity.this.finish();
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
private final class Editor {
- private final EditText mCopiesEditText;
+ private static final int UI_NONE = 0;
+ private static final int UI_EDITING_PRINT_JOB = 1;
+ private static final int UI_GENERATING_PRINT_JOB = 2;
- private final TextView mRangeTitle;
- private final EditText mRangeEditText;
+ private EditText mCopiesEditText;
- private final Spinner mDestinationSpinner;
+ private TextView mRangeTitle;
+ private EditText mRangeEditText;
+
+ private Spinner mDestinationSpinner;
private final DestinationAdapter mDestinationSpinnerAdapter;
- private final Spinner mMediaSizeSpinner;
+ private Spinner mMediaSizeSpinner;
private final ArrayAdapter> mMediaSizeSpinnerAdapter;
- private final Spinner mColorModeSpinner;
+ private Spinner mColorModeSpinner;
private final ArrayAdapter> mColorModeSpinnerAdapter;
- private final Spinner mOrientationSpinner;
+ private Spinner mOrientationSpinner;
private final ArrayAdapter> mOrientationSpinnerAdapter;
- private final Spinner mRangeOptionsSpinner;
+ private Spinner mRangeOptionsSpinner;
private final ArrayAdapter> mRangeOptionsSpinnerAdapter;
private final SimpleStringSplitter mStringCommaSplitter =
new SimpleStringSplitter(',');
- private final View mContentContainer;
+ private View mContentContainer;
- private final Button mPrintButton;
+ private Button mPrintButton;
private final OnItemSelectedListener mOnItemSelectedListener =
new AdapterView.OnItemSelectedListener() {
@@ -644,18 +738,31 @@ public class PrintJobConfigActivity extends Activity {
mIgnoreNextDestinationChange = false;
return;
}
+ if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
+ mIgnoreNextDestinationChange = true;
+ mDestinationSpinner.setSelection(0);
+ Intent intent = new Intent(PrintJobConfigActivity.this,
+ SelectPrinterActivity.class);
+ startActivityForResult(intent, ACTIVITY_REQUEST_SELECT_PRINTER);
+ return;
+ }
+ mWaitingForPrinterCapabilities = false;
mCurrPrintAttributes.clear();
PrinterInfo printer = (PrinterInfo) mDestinationSpinnerAdapter
.getItem(position);
if (printer != null) {
- PrintSpooler.peekInstance().setPrintJobPrinterNoPersistence(
+ PrintSpoolerService.peekInstance().setPrintJobPrinterNoPersistence(
mPrintJobId, printer);
PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
if (capabilities == null) {
List printerIds = new ArrayList();
printerIds.add(printer.getId());
- final int index = mAvailablePrinters.getItemIndex(printer);
- mAvailablePrinters.refreshItem(index);
+ FusedPrintersProvider printersLoader = (FusedPrintersProvider)
+ (Loader>) getLoaderManager().getLoader(
+ LOADER_ID_PRINTERS_LOADER);
+ if (printersLoader != null) {
+ printersLoader.refreshPrinter(printer.getId());
+ }
mWaitingForPrinterCapabilities = true;
//TODO: We need a timeout for the update.
} else {
@@ -668,6 +775,31 @@ public class PrintJobConfigActivity extends Activity {
}
}
}
+
+ // The printer changed so we want to start with a clean slate
+ // for the print options and let them be populated from the
+ // printer capabilities and use the printer defaults.
+ if (!mMediaSizeSpinnerAdapter.isEmpty()) {
+ mIgnoreNextMediaSizeChange = true;
+ mMediaSizeSpinnerAdapter.clear();
+ }
+ if (!mColorModeSpinnerAdapter.isEmpty()) {
+ mIgnoreNextColorModeChange = true;
+ mColorModeSpinnerAdapter.clear();
+ }
+ if (!mOrientationSpinnerAdapter.isEmpty()) {
+ mIgnoreNextOrientationChange = true;
+ mOrientationSpinnerAdapter.clear();
+ }
+ if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
+ mIgnoreNextRangeOptionChange = true;
+ mRangeOptionsSpinner.setSelection(0);
+ }
+ if (!TextUtils.isEmpty(mCopiesEditText.getText())) {
+ mIgnoreNextCopiesChange = true;
+ mCopiesEditText.setText(MIN_COPIES_STRING);
+ }
+
updateUi();
} else if (spinner == mMediaSizeSpinner) {
if (mIgnoreNextMediaSizeChange) {
@@ -753,7 +885,8 @@ public class PrintJobConfigActivity extends Activity {
}
mCopiesEditText.setError(null);
- PrintSpooler.peekInstance().setPrintJobCopiesNoPersistence(mPrintJobId, copies);
+ PrintSpoolerService.peekInstance().setPrintJobCopiesNoPersistence(
+ mPrintJobId, copies);
updateUi();
if (hadErrors && !hasErrors() && printAttributesChanged()) {
@@ -832,22 +965,25 @@ public class PrintJobConfigActivity extends Activity {
private boolean mWaitingForPrinterCapabilities;
+ private int mCurrentUi = UI_NONE;
+
public Editor() {
- // Content container
- mContentContainer = findViewById(R.id.content_container);
-
- // Copies
- mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
- mCopiesEditText.setText(String.valueOf(MIN_COPIES));
- PrintSpooler.peekInstance().setPrintJobCopiesNoPersistence(mPrintJobId, MIN_COPIES);
- mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
- mCopiesEditText.selectAll();
-
// Destination.
- mDestinationSpinnerAdapter = new DestinationAdapter(mAvailablePrinters);
+ mDestinationSpinnerAdapter = new DestinationAdapter();
mDestinationSpinnerAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
+ final int selectedPosition = mDestinationSpinner.getSelectedItemPosition();
+ if (mDestinationSpinnerAdapter.getCount() > 0) {
+ // Make sure we select the first printer if we have data.
+ if (selectedPosition == AdapterView.INVALID_POSITION) {
+ mDestinationSpinner.setSelection(0);
+ }
+ } else {
+ // Make sure we select no printer if we have no data.
+ mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION);
+ }
+
// Maybe we did not have capabilities when the current printer was
// selected, but now the selected printer has capabilities. Generate
// a fake selection so the code in the selection change handling takes
@@ -855,13 +991,9 @@ public class PrintJobConfigActivity extends Activity {
if (mWaitingForPrinterCapabilities) {
mWaitingForPrinterCapabilities = false;
PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
- if (printer != null) {
- if (printer.getCapabilities() != null) {
- final int selectedPosition =
- mDestinationSpinner.getSelectedItemPosition();
- mOnItemSelectedListener.onItemSelected(mDestinationSpinner, null,
- selectedPosition, selectedPosition);
- }
+ if (printer != null && printer.getCapabilities() != null) {
+ mOnItemSelectedListener.onItemSelected(mDestinationSpinner, null,
+ selectedPosition, selectedPosition);
}
}
updateUi();
@@ -872,41 +1004,23 @@ public class PrintJobConfigActivity extends Activity {
updateUi();
}
});
- mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
- mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
- mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Media size.
- mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
mMediaSizeSpinnerAdapter = new ArrayAdapter>(
PrintJobConfigActivity.this,
R.layout.spinner_dropdown_item, R.id.title);
- mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
- mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Color mode.
- mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
mColorModeSpinnerAdapter = new ArrayAdapter>(
PrintJobConfigActivity.this,
R.layout.spinner_dropdown_item, R.id.title);
- mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
- mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Orientation
- mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
mOrientationSpinnerAdapter = new ArrayAdapter>(
PrintJobConfigActivity.this,
R.layout.spinner_dropdown_item, R.id.title);
- mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
- mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
- // Range
- mRangeTitle = (TextView) findViewById(R.id.page_range_title);
- mRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
- mRangeEditText.addTextChangedListener(mRangeTextWatcher);
// Range options
- mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
mRangeOptionsSpinnerAdapter = new ArrayAdapter>(
PrintJobConfigActivity.this,
R.layout.spinner_dropdown_item, R.id.title);
@@ -919,26 +1033,28 @@ public class PrintJobConfigActivity extends Activity {
mRangeOptionsSpinnerAdapter.add(new SpinnerItem(
rangeOptionsValues[i], rangeOptionsLabels[i]));
}
- mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
- if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
- mIgnoreNextRangeOptionChange = true;
- mRangeOptionsSpinner.setSelection(0);
- }
-
- // Print button
- mPrintButton = (Button) findViewById(R.id.print_button);
- mPrintButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mEditor.confirmPrint();
- mController.update();
- showGeneratingPrintJobUi();
- }
- });
+ showUi(UI_EDITING_PRINT_JOB, null);
+ bindUi();
updateUi();
}
+ public void selectPrinter(PrinterId printerId) {
+ final int printerCount = mDestinationSpinnerAdapter.getCount();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo printer = (PrinterInfo) mDestinationSpinnerAdapter.getItem(i);
+ if (printer.getId().equals(printerId)) {
+ mDestinationSpinner.setSelection(i);
+ return;
+ }
+ }
+ }
+
+ public boolean isPrintingToPdf() {
+ return mDestinationSpinner.getSelectedItem()
+ == mDestinationSpinnerAdapter.mFakePdfPrinter;
+ }
+
public boolean shouldCloseOnTouch(MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_DOWN) {
return false;
@@ -966,19 +1082,104 @@ public class PrintJobConfigActivity extends Activity {
}
public boolean isShwoingGeneratingPrintJobUi() {
- return (findViewById(R.id.content_generating) != null);
+ return (mCurrentUi == UI_GENERATING_PRINT_JOB);
}
- private void showGeneratingPrintJobUi() {
- // Find everything we will shuffle around.
- final ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
- final View contentEditing = contentContainer.findViewById(R.id.content_editing);
- final View contentGenerating = getLayoutInflater().inflate(
- R.layout.print_job_config_activity_content_generating,
- contentContainer, false);
+ public void showUi(int ui, final Runnable postSwitchCallback) {
+ if (ui == UI_NONE) {
+ throw new IllegalStateException("cannot remove the ui");
+ }
- // Wire the cancel action.
- Button cancelButton = (Button) contentGenerating.findViewById(R.id.cancel_button);
+ if (mCurrentUi == ui) {
+ return;
+ }
+
+ switch (mCurrentUi) {
+ case UI_NONE: {
+ switch (ui) {
+ case UI_EDITING_PRINT_JOB: {
+ doUiSwitch(R.layout.print_job_config_activity_content_editing);
+ registerPrintButtonClickListener();
+ if (postSwitchCallback != null) {
+ postSwitchCallback.run();
+ }
+ } break;
+
+ case UI_GENERATING_PRINT_JOB: {
+ doUiSwitch(R.layout.print_job_config_activity_content_generating);
+ registerCancelButtonClickListener();
+ if (postSwitchCallback != null) {
+ postSwitchCallback.run();
+ }
+ } break;
+ }
+ } break;
+
+ case UI_EDITING_PRINT_JOB: {
+ switch (ui) {
+ case UI_GENERATING_PRINT_JOB: {
+ animateUiSwitch(R.layout.print_job_config_activity_content_generating,
+ new Runnable() {
+ @Override
+ public void run() {
+ registerCancelButtonClickListener();
+ if (postSwitchCallback != null) {
+ postSwitchCallback.run();
+ }
+ }
+ });
+ } break;
+ }
+ } break;
+
+ case UI_GENERATING_PRINT_JOB: {
+ switch (ui) {
+ case UI_EDITING_PRINT_JOB: {
+ animateUiSwitch(R.layout.print_job_config_activity_content_editing,
+ new Runnable() {
+ @Override
+ public void run() {
+ registerPrintButtonClickListener();
+ if (postSwitchCallback != null) {
+ postSwitchCallback.run();
+ }
+ }
+ });
+ } break;
+ }
+ } break;
+ }
+
+ mCurrentUi = ui;
+ }
+
+ private void registerPrintButtonClickListener() {
+ Button printButton = (Button) findViewById(R.id.print_button);
+ printButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
+ if (printer != null) {
+ mEditor.confirmPrint();
+ mController.update();
+ if (!printer.equals(mDestinationSpinnerAdapter.mFakePdfPrinter)) {
+ FusedPrintersProvider printersLoader = (FusedPrintersProvider)
+ (Loader>) getLoaderManager().getLoader(
+ LOADER_ID_PRINTERS_LOADER);
+ if (printersLoader != null) {
+ printersLoader.addHistoricalPrinter(printer);
+ }
+ }
+ } else {
+ mEditor.cancel();
+ PrintJobConfigActivity.this.finish();
+ }
+ }
+ });
+ }
+
+ private void registerCancelButtonClickListener() {
+ Button cancelButton = (Button) findViewById(R.id.cancel_button);
cancelButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -988,24 +1189,38 @@ public class PrintJobConfigActivity extends Activity {
mEditor.cancel();
}
});
+ }
+
+ private void doUiSwitch(int showLayoutId) {
+ ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
+ contentContainer.removeAllViews();
+ getLayoutInflater().inflate(showLayoutId, contentContainer, true);
+ }
+
+ private void animateUiSwitch(int showLayoutId, final Runnable postAnimateCommand) {
+ // Find everything we will shuffle around.
+ final ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
+ final View hidingView = contentContainer.getChildAt(0);
+ final View showingView = getLayoutInflater().inflate(showLayoutId,
+ null, false);
// First animation - fade out the old content.
- contentEditing.animate().alpha(0.0f).withLayer().withEndAction(new Runnable() {
+ hidingView.animate().alpha(0.0f).withLayer().withEndAction(new Runnable() {
@Override
public void run() {
- contentEditing.setVisibility(View.INVISIBLE);
+ hidingView.setVisibility(View.INVISIBLE);
// Prepare the new content with correct size and alpha.
- contentGenerating.setMinimumWidth(contentContainer.getWidth());
- contentGenerating.setAlpha(0.0f);
+ showingView.setMinimumWidth(contentContainer.getWidth());
+ showingView.setAlpha(0.0f);
- // Compute how to much shrink the container to fit around the new content.
+ // Compute how to much shrink /stretch the content.
final int widthSpec = MeasureSpec.makeMeasureSpec(
- contentContainer.getWidth(), MeasureSpec.AT_MOST);
+ contentContainer.getWidth(), MeasureSpec.UNSPECIFIED);
final int heightSpec = MeasureSpec.makeMeasureSpec(
- contentContainer.getHeight(), MeasureSpec.AT_MOST);
- contentGenerating.measure(widthSpec, heightSpec);
- final float scaleY = (float) contentGenerating.getMeasuredHeight()
+ contentContainer.getHeight(), MeasureSpec.UNSPECIFIED);
+ showingView.measure(widthSpec, heightSpec);
+ final float scaleY = (float) showingView.getMeasuredHeight()
/ (float) contentContainer.getHeight();
// Second animation - resize the container.
@@ -1016,10 +1231,16 @@ public class PrintJobConfigActivity extends Activity {
// Swap the old and the new content.
contentContainer.removeAllViews();
contentContainer.setScaleY(1.0f);
- contentContainer.addView(contentGenerating);
+ contentContainer.addView(showingView);
// Third animation - show the new content.
- contentGenerating.animate().withLayer().alpha(1.0f);
+ showingView.animate().withLayer().alpha(1.0f).withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ postAnimateCommand.run();
+ }
+ });
}
});
}
@@ -1028,10 +1249,6 @@ public class PrintJobConfigActivity extends Activity {
public void initialize() {
mEditorState = EDITOR_STATE_INITIALIZED;
- if (mDestinationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) {
- mIgnoreNextDestinationChange = true;
- mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION);
- }
}
public boolean isCancelled() {
@@ -1054,11 +1271,7 @@ public class PrintJobConfigActivity extends Activity {
public void confirmPrint() {
mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
- PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
- if (printer != null) {
- mFavoritePrinters.addPrinter(printer);
- }
- updateUi();
+ showUi(UI_GENERATING_PRINT_JOB, null);
}
public boolean isPreviewConfirmed() {
@@ -1104,7 +1317,79 @@ public class PrintJobConfigActivity extends Activity {
return ALL_PAGES_ARRAY;
}
+ private void bindUi() {
+ if (mCurrentUi != UI_EDITING_PRINT_JOB) {
+ return;
+ }
+
+ // Content container
+ mContentContainer = findViewById(R.id.content_container);
+
+ // Copies
+ mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
+ mCopiesEditText.setText(MIN_COPIES_STRING);
+ mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
+ mCopiesEditText.selectAll();
+ if (!TextUtils.equals(mCopiesEditText.getText(), MIN_COPIES_STRING)) {
+ mIgnoreNextCopiesChange = true;
+ }
+ PrintSpoolerService.peekInstance().setPrintJobCopiesNoPersistence(
+ mPrintJobId, MIN_COPIES);
+
+ // Destination.
+ mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
+ mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
+ mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ if (mDestinationSpinnerAdapter.getCount() > 0) {
+ mIgnoreNextDestinationChange = true;
+ }
+
+ // Media size.
+ mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
+ mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ if (mMediaSizeSpinnerAdapter.getCount() > 0) {
+ mIgnoreNextMediaSizeChange = true;
+ }
+
+ // Color mode.
+ mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
+ mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
+ mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ if (mColorModeSpinnerAdapter.getCount() > 0) {
+ mIgnoreNextColorModeChange = true;
+ }
+
+ // Orientation
+ mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
+ mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
+ mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ if (mOrientationSpinnerAdapter.getCount() > 0) {
+ mIgnoreNextOrientationChange = true;
+ }
+
+ // Range
+ mRangeTitle = (TextView) findViewById(R.id.page_range_title);
+ mRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
+ mRangeEditText.addTextChangedListener(mRangeTextWatcher);
+
+ // Range options
+ mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
+ mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
+ mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ if (mRangeOptionsSpinnerAdapter.getCount() > 0) {
+ mIgnoreNextRangeOptionChange = true;
+ }
+
+ // Print button
+ mPrintButton = (Button) findViewById(R.id.print_button);
+ registerPrintButtonClickListener();
+ }
+
public void updateUi() {
+ if (mCurrentUi != UI_EDITING_PRINT_JOB) {
+ return;
+ }
if (isPrintConfirmed() || isPreviewConfirmed() || isCancelled()) {
mDestinationSpinner.setEnabled(false);
mCopiesEditText.setEnabled(false);
@@ -1119,14 +1404,20 @@ public class PrintJobConfigActivity extends Activity {
return;
}
+ // If a printer with capabilities is selected, then we enabled all options.
+ boolean allOptionsEnabled = false;
final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+ if (selectedIndex >= 0) {
+ Object item = mDestinationSpinnerAdapter.getItem(selectedIndex);
+ if (item instanceof PrinterInfo) {
+ PrinterInfo printer = (PrinterInfo) item;
+ if (printer.getCapabilities() != null) {
+ allOptionsEnabled = true;
+ }
+ }
+ }
- if (selectedIndex < 0 || ((PrinterInfo) mDestinationSpinnerAdapter.getItem(
- selectedIndex)).getCapabilities() == null) {
-
- // Destination
- mDestinationSpinner.setEnabled(false);
-
+ if (!allOptionsEnabled) {
String minCopiesString = String.valueOf(MIN_COPIES);
if (!TextUtils.equals(mCopiesEditText.getText(), minCopiesString)) {
mIgnoreNextCopiesChange = true;
@@ -1183,9 +1474,6 @@ public class PrintJobConfigActivity extends Activity {
PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
printer.getCapabilities().getDefaults(defaultAttributes);
- // Destination
- mDestinationSpinner.setEnabled(true);
-
// Copies
mCopiesEditText.setEnabled(true);
@@ -1223,6 +1511,7 @@ public class PrintJobConfigActivity extends Activity {
}
}
}
+ mMediaSizeSpinner.setEnabled(true);
// Color mode.
final int colorModes = capabilities.getColorModes();
@@ -1271,6 +1560,7 @@ public class PrintJobConfigActivity extends Activity {
}
}
}
+ mColorModeSpinner.setEnabled(true);
// Orientation.
final int orientations = capabilities.getOrientations();
@@ -1321,20 +1611,25 @@ public class PrintJobConfigActivity extends Activity {
}
}
}
+ mOrientationSpinner.setEnabled(true);
// Range options
PrintDocumentInfo info = mDocument.info;
if (info != null && (info.getPageCount() > 1
|| info.getPageCount() == PrintDocumentInfo.PAGE_COUNT_UNKNOWN)) {
mRangeOptionsSpinner.setEnabled(true);
- if (mRangeOptionsSpinner.getSelectedItemPosition() > 0
- && !mRangeEditText.isEnabled()) {
- mRangeEditText.setEnabled(true);
- mRangeEditText.setVisibility(View.VISIBLE);
- mRangeEditText.requestFocus();
- InputMethodManager imm = (InputMethodManager)
- getSystemService(INPUT_METHOD_SERVICE);
- imm.showSoftInput(mRangeEditText, 0);
+ if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
+ if (!mRangeEditText.isEnabled()) {
+ mRangeEditText.setEnabled(true);
+ mRangeEditText.setVisibility(View.VISIBLE);
+ mRangeEditText.requestFocus();
+ InputMethodManager imm = (InputMethodManager)
+ getSystemService(INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mRangeEditText, 0);
+ }
+ } else {
+ mRangeEditText.setEnabled(false);
+ mRangeEditText.setVisibility(View.INVISIBLE);
}
final int pageCount = mDocument.info.getPageCount();
mRangeTitle.setText(getString(R.string.label_pages,
@@ -1352,6 +1647,7 @@ public class PrintJobConfigActivity extends Activity {
mRangeEditText.setEnabled(false);
mRangeEditText.setVisibility(View.INVISIBLE);
}
+ mRangeOptionsSpinner.setEnabled(true);
// Print/Print preview
if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1
@@ -1378,6 +1674,7 @@ public class PrintJobConfigActivity extends Activity {
mCopiesEditText.selectAll();
mCopiesEditText.requestFocus();
}
+ mCopiesEditText.setEnabled(true);
}
}
@@ -1407,38 +1704,51 @@ public class PrintJobConfigActivity extends Activity {
}
}
- private final class DestinationAdapter extends BaseAdapter {
- private final AvailablePrinterProvider mProvider;
+ private final class DestinationAdapter extends BaseAdapter
+ implements LoaderManager.LoaderCallbacks>{
+ private final List mPrinters = new ArrayList();
- private final DataSetObserver mObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
+ public final PrinterInfo mFakePdfPrinter;
- @Override
- public void onInvalidated() {
- notifyDataSetInvalidated();
- }
- };
-
- public DestinationAdapter(AvailablePrinterProvider provider) {
- mProvider = provider;
- mProvider.registerObserver(mObserver);
+ public DestinationAdapter() {
+ getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
+ mFakePdfPrinter = createFakePdfPrinter();
}
@Override
public int getCount() {
- return mProvider.getItemCount();
+ return Math.max(Math.min(mPrinters.size(), DEST_ADAPTER_MAX_ITEM_COUNT),
+ DEST_ADAPTER_MIN_ITEM_COUNT);
}
@Override
public Object getItem(int position) {
- return mProvider.getItemAt(position);
+ if (position == DEST_ADAPTER_POSITION_SAVE_AS_PDF) {
+ return mFakePdfPrinter;
+ }
+ if (!mPrinters.isEmpty()) {
+ if (position < DEST_ADAPTER_POSITION_SAVE_AS_PDF) {
+ return mPrinters.get(position);
+ } else if (position > DEST_ADAPTER_POSITION_SAVE_AS_PDF
+ && position < getCount() - 1) {
+ return mPrinters.get(position - 1);
+ }
+ }
+ return null;
}
@Override
public long getItemId(int position) {
+ if (position == DEST_ADAPTER_POSITION_SAVE_AS_PDF) {
+ return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
+ }
+ if (mPrinters.isEmpty()) {
+ if (position == DEST_ADAPTER_POSITION_SEARCHING_FOR_PRINTERS) {
+ return DEST_ADAPTER_ITEM_ID_SEARCHING_FOR_PRINTERS;
+ }
+ } else if (position == getCount() - 1) {
+ return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+ }
return position;
}
@@ -1455,24 +1765,92 @@ public class PrintJobConfigActivity extends Activity {
R.layout.spinner_dropdown_item, parent, false);
}
- PrinterInfo printerInfo = mProvider.getItemAt(position);
- TextView title = (TextView) convertView.findViewById(R.id.title);
- title.setText(printerInfo.getName());
+ CharSequence title = null;
+ CharSequence subtitle = null;
- try {
- TextView subtitle = (TextView)
- convertView.findViewById(R.id.subtitle);
- PackageManager pm = getPackageManager();
- PackageInfo packageInfo = pm.getPackageInfo(
- printerInfo.getId().getServiceName().getPackageName(), 0);
- subtitle.setText(packageInfo.applicationInfo.loadLabel(pm));
- subtitle.setVisibility(View.VISIBLE);
- } catch (NameNotFoundException nnfe) {
- /* ignore */
+ if (mPrinters.isEmpty()
+ && position == DEST_ADAPTER_POSITION_SEARCHING_FOR_PRINTERS) {
+ title = getString(R.string.searching_for_printers);
+ } else {
+ if (position == DEST_ADAPTER_POSITION_SAVE_AS_PDF) {
+ PrinterInfo printer = (PrinterInfo) getItem(position);
+ title = printer.getName();
+ } else if (position == getCount() - 1) {
+ title = getString(R.string.all_printers);
+ } else {
+ PrinterInfo printer = (PrinterInfo) getItem(position);
+ title = printer.getName();
+ try {
+ PackageInfo packageInfo = getPackageManager().getPackageInfo(
+ printer.getId().getServiceName().getPackageName(), 0);
+ subtitle = packageInfo.applicationInfo.loadLabel(getPackageManager());
+ } 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> onCreateLoader(int id, Bundle args) {
+ if (id == LOADER_ID_PRINTERS_LOADER) {
+ return new FusedPrintersProvider(PrintJobConfigActivity.this);
+ }
+ return null;
+ }
+
+ @Override
+ public void onLoadFinished(Loader> loader,
+ List printers) {
+ mPrinters.clear();
+ mPrinters.addAll(printers);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onLoaderReset(Loader> loader) {
+ mPrinters.clear();
+ notifyDataSetInvalidated();
+ }
+
+ private PrinterInfo createFakePdfPrinter() {
+ PrinterId printerId = new PrinterId(getComponentName(), "PDF printer");
+
+ PrinterCapabilitiesInfo capabilities =
+ new PrinterCapabilitiesInfo.Builder(printerId)
+ .addMediaSize(MediaSize.createMediaSize(getPackageManager(),
+ MediaSize.ISO_A4), true)
+ .addMediaSize(MediaSize.createMediaSize(getPackageManager(),
+ MediaSize.NA_LETTER), false)
+ .addResolution(new Resolution("PDF resolution", "PDF resolution",
+ 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR
+ | PrintAttributes.COLOR_MODE_MONOCHROME,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .setOrientations(PrintAttributes.ORIENTATION_PORTRAIT
+ | PrintAttributes.ORIENTATION_LANDSCAPE,
+ PrintAttributes.ORIENTATION_PORTRAIT)
+ .create();
+
+ return new PrinterInfo.Builder(printerId, getString(R.string.save_as_pdf),
+ PrinterInfo.STATUS_READY)
+ .setCapabilities(capabilities)
+ .create();
+ }
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
deleted file mode 100644
index c2cf65ee7ffab..0000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ /dev/null
@@ -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 mPrintJobs = new ArrayList();
-
- 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 getPrintJobInfos(ComponentName componentName,
- int state, int appId) {
- List 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();
- }
- 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() {
- @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() {
- @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() {
- @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 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 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();
- }
- 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;
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 4fab4f886d5cc..fda64c9e6a0b0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -21,9 +21,8 @@ import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
-import android.os.Handler;
+import android.os.AsyncTask;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -32,14 +31,38 @@ import android.print.IPrintDocumentAdapter;
import android.print.IPrintSpooler;
import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
-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.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+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;
/**
@@ -48,22 +71,65 @@ import java.util.List;
*/
public final class PrintSpoolerService extends Service {
+ private static final String LOG_TAG = "PrintSpoolerService";
+
+ 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 long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
- private static final String LOG_TAG = "PrintSpoolerService";
+ private static final String PRINT_FILE_EXTENSION = "pdf";
+
+ private static final Object sLock = new Object();
+
+ private final Object mLock = new Object();
+
+ private final List mPrintJobs = new ArrayList();
+
+ private static PrintSpoolerService sInstance;
+
+ private static int sPrintJobIdCounter;
private Intent mStartPrintJobConfigActivityIntent;
private IPrintSpoolerClient mClient;
- private Handler mHandler;
+ private HandlerCaller mHandlerCaller;
+
+ private PersistenceManager mPersistanceManager;
+
+ private NotificationController mNotificationController;
+
+ private PrinterDiscoverySession mDiscoverySession;
+
+ public static PrintSpoolerService peekInstance() {
+ synchronized (sLock) {
+ return sInstance;
+ }
+ }
@Override
public void onCreate() {
super.onCreate();
mStartPrintJobConfigActivityIntent = new Intent(PrintSpoolerService.this,
PrintJobConfigActivity.class);
- mHandler = new MyHandler(getMainLooper());
+ mHandlerCaller = new HandlerCaller(this, getMainLooper(),
+ new HandlerCallerCallback(), false);
+
+ mPersistanceManager = new PersistenceManager();
+ mNotificationController = new NotificationController(PrintSpoolerService.this);
+
+ synchronized (mLock) {
+ mPersistanceManager.readStateLocked();
+ handleReadPrintJobsLocked();
+ }
+
+ synchronized (sLock) {
+ sInstance = this;
+ }
}
@Override
@@ -72,10 +138,10 @@ public final class PrintSpoolerService extends Service {
@Override
public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
ComponentName componentName, int state, int appId, int sequence)
- throws RemoteException {
+ throws RemoteException {
List printJobs = null;
try {
- printJobs = PrintSpooler.peekInstance().getPrintJobInfos(
+ printJobs = PrintSpoolerService.this.getPrintJobInfos(
componentName, state, appId);
} finally {
callback.onGetPrintJobInfosResult(printJobs, sequence);
@@ -87,7 +153,7 @@ public final class PrintSpoolerService extends Service {
int appId, int sequence) throws RemoteException {
PrintJobInfo printJob = null;
try {
- printJob = PrintSpooler.peekInstance().getPrintJobInfo(printJobId, appId);
+ printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
} finally {
callback.onGetPrintJobInfoResult(printJob, sequence);
}
@@ -98,11 +164,11 @@ public final class PrintSpoolerService extends Service {
public void createPrintJob(String printJobName, IPrintClient client,
IPrintDocumentAdapter printAdapter, PrintAttributes attributes,
IPrintSpoolerCallbacks callback, int appId, int sequence)
- throws RemoteException {
+ throws RemoteException {
PrintJobInfo printJob = null;
try {
- printJob = PrintSpooler.peekInstance().createPrintJob(printJobName, client,
- attributes, appId);
+ printJob = PrintSpoolerService.this.createPrintJob(
+ printJobName, client, attributes, appId);
if (printJob != null) {
Intent intent = mStartPrintJobConfigActivityIntent;
intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_DOCUMENT_ADAPTER,
@@ -113,13 +179,12 @@ public final class PrintSpoolerService extends Service {
IntentSender sender = PendingIntent.getActivity(
PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+ | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = client;
- args.arg2 = sender;
- mHandler.obtainMessage(MyHandler.MSG_START_PRINT_JOB_CONFIG_ACTIVITY,
- args).sendToTarget();
+ Message message = mHandlerCaller.obtainMessageOO(
+ HandlerCallerCallback.MSG_START_PRINT_JOB_CONFIG_ACTIVITY,
+ client, sender);
+ mHandlerCaller.executeOrSendMessage(message);
}
} finally {
callback.onCreatePrintJobResult(printJob, sequence);
@@ -127,11 +192,11 @@ public final class PrintSpoolerService extends Service {
}
@Override
- public void setPrintJobState(int printJobId, int state, CharSequence error,
+ public void setPrintJobState(int printJobId, int state, String error,
IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
boolean success = false;
try {
- success = PrintSpooler.peekInstance().setPrintJobState(
+ success = PrintSpoolerService.this.setPrintJobState(
printJobId, state, error);
} finally {
callback.onSetPrintJobStateResult(success, sequece);
@@ -143,7 +208,7 @@ public final class PrintSpoolerService extends Service {
IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
boolean success = false;
try {
- success = PrintSpooler.peekInstance().setPrintJobTag(printJobId, tag);
+ success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
} finally {
callback.onSetPrintJobTagResult(success, sequece);
}
@@ -151,60 +216,191 @@ public final class PrintSpoolerService extends Service {
@Override
public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
- PrintSpooler.peekInstance().writePrintJobData(fd, printJobId);
+ PrintSpoolerService.this.writePrintJobData(fd, printJobId);
}
@Override
public void setClient(IPrintSpoolerClient client) {
- mHandler.obtainMessage(MyHandler.MSG_SET_CLIENT, client).sendToTarget();
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_SET_CLIENT, client);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ @Override
+ public void onPrintersAdded(List printers) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINTERS_ADDED, printers);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ @Override
+ public void onPrintersRemoved(List printerIds) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINTERS_REMOVED, printerIds);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ @Override
+ public void onPrintersUpdated(List printers) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINTERS_UPDATED, printers);
+ mHandlerCaller.executeOrSendMessage(message);
}
};
}
- public void onPrintJobQueued(PrintJobInfo printJob) {
- mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
- printJob).sendToTarget();
+ public void createPrinterDiscoverySession() {
+ Message message = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
+ mHandlerCaller.executeOrSendMessage(message);
}
- public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
- mHandler.obtainMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
- observer).sendToTarget();
+ public void destroyPrinterDiscoverySession() {
+ Message message = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
+ mHandlerCaller.executeOrSendMessage(message);
}
- public void onAllPrintJobsForServiceHandled(ComponentName service) {
- mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
- service).sendToTarget();
+ public void startPrinterDiscovery(List priorityList) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_START_PRINTER_DISCOVERY, priorityList);
+ mHandlerCaller.executeOrSendMessage(message);
}
- public void onAllPrintJobsHandled() {
- mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED);
+ public void stopPrinterDiscovery() {
+ Message message = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_STOP_PRINTER_DISCOVERY);
+ mHandlerCaller.executeOrSendMessage(message);
}
- private final class MyHandler extends Handler {
- public static final int MSG_SET_CLIENT = 1;
- public static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 2;
- public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 3;
- public static final int MSG_ON_PRINT_JOB_QUEUED = 5;
- public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 6;
- public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 7;
- public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 9;
+ public void requestPrinterUpdate(PrinterId pritnerId) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_REQUEST_PRINTER_UPDATE, pritnerId);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
- public MyHandler(Looper looper) {
- super(looper, null, false);
- }
+
+ private void sendOnPrintJobQueued(PrintJobInfo printJob) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ private void sendOnAllPrintJobsHandled() {
+ Message message = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ private final class HandlerCallerCallback implements HandlerCaller.Callback {
+ public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
+ public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
+ public static final int MSG_START_PRINTER_DISCOVERY = 3;
+ public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
+ public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
+
+ public static final int MSG_ON_PRINTERS_ADDED = 6;
+ public static final int MSG_ON_PRINTERS_REMOVED = 7;
+ public static final int MSG_ON_PRINTERS_UPDATED = 8;
+
+ public static final int MSG_SET_CLIENT = 9;
+ public static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 10;
+ public static final int MSG_ON_PRINT_JOB_QUEUED = 11;
+ public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 12;
+ public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 13;
+ public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 14;
@Override
- public void handleMessage(Message message) {
+ @SuppressWarnings("unchecked")
+ public void executeMessage(Message message) {
switch (message.what) {
+ case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
+ final IPrintSpoolerClient client;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.createPrinterDiscoverySession();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error creating printer discovery session.", re);
+ }
+ }
+ } break;
+
+ case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
+ final IPrintSpoolerClient client;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.destroyPrinterDiscoverySession();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error destroying printer discovery session.", re);
+ }
+ }
+ } break;
+
+ case MSG_START_PRINTER_DISCOVERY: {
+ final IPrintSpoolerClient client;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ List priorityList = (ArrayList) message.obj;
+ try {
+ client.startPrinterDiscovery(priorityList);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error starting printer discovery.", re);
+ }
+ }
+ } break;
+
+ case MSG_STOP_PRINTER_DISCOVERY: {
+ final IPrintSpoolerClient client;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.stopPrinterDiscovery();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error stopping printer discovery.", re);
+ }
+ }
+ } break;
+
+ case MSG_REQUEST_PRINTER_UPDATE: {
+ final IPrintSpoolerClient client;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ PrinterId printerId = (PrinterId) message.obj;
+ try {
+ client.requestPrinterUpdate(printerId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error requesing printer update.", re);
+ }
+ }
+ } break;
+
case MSG_SET_CLIENT: {
- mClient = (IPrintSpoolerClient) message.obj;
- if (mClient != null) {
- PrintSpooler.createInstance(PrintSpoolerService.this);
- mHandler.sendEmptyMessageDelayed(
- MyHandler.MSG_CHECK_ALL_PRINTJOBS_HANDLED,
- CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
- } else {
- PrintSpooler.destroyInstance();
+ synchronized (mLock) {
+ mClient = (IPrintSpoolerClient) message.obj;
+ if (mClient != null) {
+ Message msg = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
+ mHandlerCaller.sendMessageDelayed(msg,
+ CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
+ }
}
} break;
@@ -220,18 +416,6 @@ public final class PrintSpoolerService extends Service {
}
} break;
- case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
- IPrinterDiscoverySessionObserver observer =
- (IPrinterDiscoverySessionObserver) message.obj;
- if (mClient != null) {
- try {
- mClient.createPrinterDiscoverySession(observer);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error creating printer discovery session.", re);
- }
- }
- } break;
-
case MSG_ON_PRINT_JOB_QUEUED: {
PrintJobInfo printJob = (PrintJobInfo) message.obj;
if (mClient != null) {
@@ -266,12 +450,948 @@ public final class PrintSpoolerService extends Service {
} break;
case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
- PrintSpooler spooler = PrintSpooler.peekInstance();
- if (spooler != null) {
- spooler.checkAllPrintJobsHandled();
+ checkAllPrintJobsHandled();
+ } break;
+
+ case MSG_ON_PRINTERS_ADDED: {
+ final PrinterDiscoverySession session;
+ synchronized (mLock) {
+ session = mDiscoverySession;
+ }
+ if (session != null) {
+ List printers = (ArrayList) message.obj;
+ session.onPrintersAdded(printers);
+ }
+ } break;
+
+ case MSG_ON_PRINTERS_REMOVED: {
+ final PrinterDiscoverySession session;
+ synchronized (mLock) {
+ session = mDiscoverySession;
+ }
+ if (session != null) {
+ List printerIds = (ArrayList) message.obj;
+ session.onPrintersRemoved(printerIds);
+ }
+ } break;
+
+ case MSG_ON_PRINTERS_UPDATED: {
+ final PrinterDiscoverySession session;
+ synchronized (mLock) {
+ session = mDiscoverySession;
+ }
+ if (session != null) {
+ List printers = (ArrayList) message.obj;
+ session.onPrintersUpdated(printers);
}
} break;
}
}
}
+
+ public List getPrintJobInfos(ComponentName componentName,
+ int state, int appId) {
+ List 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();
+ }
+ 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(String 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,
+ getString(R.string.no_connection_to_printer));
+ }
+ break;
+ }
+ }
+ }
+
+ public void checkAllPrintJobsHandled() {
+ synchronized (mLock) {
+ if (!hasActivePrintJobsLocked()) {
+ notifyOnAllPrintJobsHandled();
+ }
+ }
+ }
+
+ private void setPrinterDiscoverySessionClient(PrinterDiscoverySession session) {
+ synchronized (mLock) {
+ mDiscoverySession = session;
+ }
+ }
+
+ 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() {
+ @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(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, String 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)) {
+ sendOnAllPrintJobsForServiceHandled(service);
+ }
+ }
+ } break;
+
+ case PrintJobInfo.STATE_QUEUED: {
+ sendOnPrintJobQueued(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 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() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ sendOnAllPrintJobsHandled();
+ 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() {
+ mStatePersistFile = new AtomicFile(new File(getFilesDir(),
+ PERSIST_FILE_NAME));
+ }
+
+ public void writeStateLocked() {
+ if (!PERSISTNECE_MANAGER_ENABLED) {
+ return;
+ }
+ if (mWriteStateScheduled) {
+ return;
+ }
+ mWriteStateScheduled = true;
+ new AsyncTask() {
+ @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 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 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();
+ }
+ 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;
+ }
+ }
+
+ public static abstract class PrinterDiscoverySession {
+
+ private PrintSpoolerService mService;
+
+ private boolean mIsStarted;
+
+ public PrinterDiscoverySession() {
+ mService = PrintSpoolerService.peekInstance();
+ mService.createPrinterDiscoverySession();
+ mService.setPrinterDiscoverySessionClient(this);
+ }
+
+ public final void startPrinterDisovery(List priorityList) {
+ mIsStarted = true;
+ mService.startPrinterDiscovery(priorityList);
+ }
+
+ public final void stopPrinterDiscovery() {
+ mIsStarted = false;
+ mService.stopPrinterDiscovery();
+ }
+
+ public void requestPrinterUpdated(PrinterId printerId) {
+ mService.requestPrinterUpdate(printerId);
+ }
+
+ public final void destroy() {
+ mService.setPrinterDiscoverySessionClient(null);
+ mService.destroyPrinterDiscoverySession();
+ }
+
+ public final boolean isStarted() {
+ return mIsStarted;
+ }
+
+ public abstract void onPrintersAdded(List printers);
+
+ public abstract void onPrintersRemoved(List printerIds);
+
+ public abstract void onPrintersUpdated(List printers);
+ }
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ChoosePrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterActivity.java
similarity index 53%
rename from packages/PrintSpooler/src/com/android/printspooler/ChoosePrinterActivity.java
rename to packages/PrintSpooler/src/com/android/printspooler/SelectPrinterActivity.java
index 8b0dd66a9739d..141dbd1e27bb3 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ChoosePrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterActivity.java
@@ -17,12 +17,25 @@
package com.android.printspooler;
import android.app.Activity;
+import android.content.Intent;
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
- public void onCreate(Bundle bundle) {
- setContentView(R.layout.choose_printer_activity);
+ protected void onCreate(Bundle savedInstanceState) {
+ 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();
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
new file mode 100644
index 0000000000000..9ca3a869626a8
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
@@ -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 mAddPrinterServices =
+ new ArrayList();
+
+ 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 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 printServices = (List) (List>)
+ getArguments().getParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS);
+
+ ArrayAdapter adapter = new ArrayAdapter(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>, Filterable {
+
+ private final Object mLock = new Object();
+
+ private final List mPrinters = new ArrayList();
+
+ private final List mFilteredPrinters = new ArrayList();
+
+ 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 filteredPrinters = new ArrayList();
+ 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 printers = (List) 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> onCreateLoader(int id, Bundle args) {
+ if (id == LOADER_ID_PRINTERS_LOADER) {
+ return new FusedPrintersProvider(getActivity());
+ }
+ return null;
+ }
+
+ @Override
+ public void onLoadFinished(Loader> loader,
+ List 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> loader) {
+ synchronized (mLock) {
+ mPrinters.clear();
+ mFilteredPrinters.clear();
+ }
+ notifyDataSetInvalidated();
+ }
+ }
+}
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
index 491dddcaa9b05..5c68460146723 100644
--- a/services/java/com/android/server/print/RemotePrintService.java
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -30,8 +30,6 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.print.IPrinterDiscoverySessionController;
-import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.print.PrinterId;
@@ -79,6 +77,10 @@ final class RemotePrintService implements DeathRecipient {
private boolean mDestroyed;
+ private boolean mAllPrintJobsHandled;
+
+ private boolean mHasPrinterDiscoverySession;
+
public RemotePrintService(Context context, ComponentName componentName, int userId,
RemotePrintSpooler spooler) {
mContext = context;
@@ -97,6 +99,8 @@ final class RemotePrintService implements DeathRecipient {
private void handleDestroy() {
throwIfDestroyed();
ensureUnbound();
+ mAllPrintJobsHandled = false;
+ mHasPrinterDiscoverySession = false;
mDestroyed = true;
}
@@ -110,18 +114,27 @@ final class RemotePrintService implements DeathRecipient {
}
private void handleBinderDied() {
+ mAllPrintJobsHandled = false;
+ mHasPrinterDiscoverySession = false;
mPendingCommands.clear();
ensureUnbound();
}
private void handleOnAllPrintJobsHandled() {
throwIfDestroyed();
+
+ mAllPrintJobsHandled = true;
+
if (isBound()) {
if (DEBUG) {
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) {
throwIfDestroyed();
+
+ mAllPrintJobsHandled = false;
+
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@@ -173,20 +189,18 @@ final class RemotePrintService implements DeathRecipient {
}
}
- public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
- mHandler.obtainMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
- observer).sendToTarget();
+ public void createPrinterDiscoverySession() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
}
- private void handleCreatePrinterDiscoverySession(
- final IPrinterDiscoverySessionObserver observer) {
+ private void handleCreatePrinterDiscoverySession() {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
- handleCreatePrinterDiscoverySession(observer);
+ handleCreatePrinterDiscoverySession();
}
});
} else {
@@ -194,9 +208,126 @@ final class RemotePrintService implements DeathRecipient {
Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()");
}
try {
- mPrintService.createPrinterDiscoverySession(observer);
+ mPrintService.createPrinterDiscoverySession();
} 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 priorityList) {
+ mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
+ priorityList).sendToTarget();
+ }
+
+ private void handleStartPrinterDiscovery(final List 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 {
- public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 1;
- public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 2;
- public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
- public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 4;
- public static final int MSG_DESTROY = 6;
- public static final int MSG_BINDER_DIED = 7;
+ public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
+ public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
+ public static final int MSG_START_PRINTER_DISCOVERY = 3;
+ public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
+ public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
+ 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) {
super(looper, null, false);
}
@Override
+ @SuppressWarnings("unchecked")
public void handleMessage(Message message) {
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 priorityList = (ArrayList) 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: {
handleOnAllPrintJobsHandled();
} break;
@@ -307,13 +465,6 @@ final class RemotePrintService implements DeathRecipient {
handleOnPrintJobQueued(printJob);
} break;
- case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
- IPrinterDiscoverySessionObserver observer =
- (IPrinterDiscoverySessionObserver) message.obj;
- handleCreatePrinterDiscoverySession(new SecurePrinterDiscoverySessionObserver(
- mComponentName, observer));
- } break;
-
case MSG_DESTROY: {
handleDestroy();
} break;
@@ -363,7 +514,7 @@ final class RemotePrintService implements DeathRecipient {
}
@Override
- public boolean setPrintJobState(int printJobId, int state, CharSequence error) {
+ public boolean setPrintJobState(int printJobId, int state, String error) {
RemotePrintService service = mWeakService.get();
if (service != null) {
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
public void onPrintersAdded(List printers) {
- throwIfPrinterIdsForPrinterInfoTampered(printers);
- try {
- mDecoratedObsever.onPrintersAdded(printers);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error delegating to onPrintersAdded", re);
- }
- }
-
- @Override
- public void onPrintersUpdated(List printers) {
- throwIfPrinterIdsForPrinterInfoTampered(printers);
- try {
- mDecoratedObsever.onPrintersUpdated(printers);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error delegating to onPrintersUpdated.", re);
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.onPrintersAdded(printers);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@Override
public void onPrintersRemoved(List printerIds) {
- throwIfPrinterIdsTampered(printerIds);
- try {
- mDecoratedObsever.onPrintersRemoved(printerIds);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error delegating to onPrintersRemoved", re);
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ throwIfPrinterIdsTampered(service.mComponentName, printerIds);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.onPrintersRemoved(printerIds);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@Override
- public void setController(IPrinterDiscoverySessionController controller) {
- try {
- mDecoratedObsever.setController(controller);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error setting controller", re);
+ public void onPrintersUpdated(List printers) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.onPrintersUpdated(printers);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
- private void throwIfPrinterIdsForPrinterInfoTampered(
+ private void throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName,
List printerInfos) {
final int printerInfoCount = printerInfos.size();
for (int i = 0; i < printerInfoCount; i++) {
PrinterId printerId = printerInfos.get(i).getId();
- throwIfPrinterIdTampered(printerId);
+ throwIfPrinterIdTampered(serviceName, printerId);
}
}
- private void throwIfPrinterIdsTampered(List printerIds) {
+ private void throwIfPrinterIdsTampered(ComponentName serviceName,
+ List printerIds) {
final int printerIdCount = printerIds.size();
for (int i = 0; i < printerIdCount; 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
- || !printerId.getServiceName().equals(mComponentName)) {
+ || !printerId.getServiceName().equals(serviceName)) {
throw new IllegalArgumentException("Invalid printer id: " + printerId);
}
}
diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java
index c932e9b418f82..d261288763f85 100644
--- a/services/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -32,9 +32,10 @@ import android.print.IPrintDocumentAdapter;
import android.print.IPrintSpooler;
import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
-import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
import android.util.Slog;
import android.util.TimedRemoteCaller;
@@ -92,7 +93,11 @@ final class RemotePrintSpooler {
public static interface PrintSpoolerCallbacks {
public void onPrintJobQueued(PrintJobInfo printJob);
public void onAllPrintJobsForServiceHandled(ComponentName printService);
- public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
+ public void createPrinterDiscoverySession();
+ public void destroyPrinterDiscoverySession();
+ public void startPrinterDiscovery(List priorityList);
+ public void stopPrinterDiscovery();
+ public void requestPrinterUpdate(PrinterId printerId);
}
public RemotePrintSpooler(Context context, int userId,
@@ -209,7 +214,7 @@ final class RemotePrintSpooler {
return null;
}
- public final boolean setPrintJobState(int printJobId, int state, CharSequence error) {
+ public final boolean setPrintJobState(int printJobId, int state, String error) {
throwIfCalledOnMainThread();
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -300,6 +305,78 @@ final class RemotePrintSpooler {
}
}
+ public final void onPrintersAdded(List 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 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 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 {
synchronized (mLock) {
if (mRemoteInstance != null) {
@@ -488,7 +565,7 @@ final class RemotePrintSpooler {
}
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();
target.setPrintJobState(printJobId, status, error, mCallback, sequence);
return getResultTimed(sequence);
@@ -597,12 +674,64 @@ final class RemotePrintSpooler {
}
@Override
- public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
+ public void createPrinterDiscoverySession() {
RemotePrintSpooler spooler = mWeakSpooler.get();
if (spooler != null) {
final long identity = Binder.clearCallingIdentity();
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 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 {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index ffcc9c3057bfb..9d7cfdd97fbde 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -21,8 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
+import android.print.PrinterId;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
@@ -105,7 +105,7 @@ final class UserState implements PrintSpoolerCallbacks {
}
@Override
- public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
+ public void createPrinterDiscoverySession() {
final List services;
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -117,7 +117,73 @@ final class UserState implements PrintSpoolerCallbacks {
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
RemotePrintService service = services.get(i);
- service.createPrinterDiscoverySession(observer);
+ service.createPrinterDiscoverySession();
+ }
+ }
+
+ @Override
+ public void destroyPrinterDiscoverySession() {
+ final List services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList(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 printerIds) {
+ final List services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList(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 services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList(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);
}
}