diff options
20 files changed, 1636 insertions, 865 deletions
@@ -162,6 +162,7 @@ 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/IPrinterDiscoveryObserver.aidl \ core/java/android/print/IPrintDocumentAdapter.aidl \ core/java/android/print/IPrintClient.aidl \ core/java/android/print/IPrintManager.aidl \ diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index c1d4ae9..d2a9cdc 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -18,8 +18,6 @@ package android.os; import android.util.ArrayMap; -import java.util.HashMap; - /** * Takes care of the grunt work of maintaining a list of remote interfaces, * typically for the use of performing callbacks from a diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index 7155096..3bfd9a1 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -16,8 +16,10 @@ package android.print; +import android.print.IPrinterDiscoveryObserver; import android.print.IPrintDocumentAdapter; import android.print.IPrintClient; +import android.print.PrinterId; import android.print.PrintJobInfo; import android.print.PrintAttributes; @@ -34,4 +36,12 @@ interface IPrintManager { int appId, int userId); void cancelPrintJob(int printJobId, int appId, int userId); void restartPrintJob(int printJobId, int appId, int userId); + + void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); + void startPrinterDiscovery(in IPrinterDiscoveryObserver observer, + in List<PrinterId> priorityList, int userId); + void stopPrinterDiscovery(in IPrinterDiscoveryObserver observer, int userId); + void requestPrinterUpdate(in PrinterId printerId, int userId); + void destroyPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, + int userId); } diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl index 5c8a22a..0a77dab 100644 --- a/core/java/android/print/IPrintSpooler.aidl +++ b/core/java/android/print/IPrintSpooler.aidl @@ -18,7 +18,6 @@ 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; @@ -47,9 +46,4 @@ oneway interface IPrintSpooler { int sequence); void writePrintJobData(in ParcelFileDescriptor fd, int printJobId); void setClient(IPrintSpoolerClient client); - - // Printer discovery APIs - void onPrintersAdded(in List<PrinterInfo> printers); - void onPrintersRemoved(in List<PrinterId> printerIds); - void onPrintersUpdated(in List<PrinterInfo> printerIds); } diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl index da60120..8b511d6 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.PrinterId; import android.print.PrintJobInfo; @@ -30,11 +29,4 @@ oneway interface IPrintSpoolerClient { void onPrintJobQueued(in PrintJobInfo printJob); void onAllPrintJobsForServiceHandled(in ComponentName printService); void onAllPrintJobsHandled(); - - // Printer discovery APIs - void createPrinterDiscoverySession(); - void startPrinterDiscovery(in List<PrinterId> priorityList); - void stopPrinterDiscovery(); - void requestPrinterUpdate(in PrinterId printerId); - void destroyPrinterDiscoverySession(); } diff --git a/core/java/android/print/IPrinterDiscoveryObserver.aidl b/core/java/android/print/IPrinterDiscoveryObserver.aidl new file mode 100644 index 0000000..625f383 --- /dev/null +++ b/core/java/android/print/IPrinterDiscoveryObserver.aidl @@ -0,0 +1,32 @@ +/* + * 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.IPrintClient; +import android.print.PrinterId; +import android.print.PrinterInfo; + +/** + * Interface for observing discovered printers by a discovery session. + * + * @hide + */ +oneway interface IPrinterDiscoveryObserver { + void onPrintersAdded(in List<PrinterInfo> printers); + void onPrintersRemoved(in List<PrinterId> printerIds); + void onPrintersUpdated(in List<PrinterInfo> printerIds); +} diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 531dcb2..d3e35c3 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -204,6 +204,13 @@ public final class PrintManager { return null; } + /** + * @hide + */ + public PrinterDiscoverySession createPrinterDiscoverySession() { + return new PrinterDiscoverySession(mService, mContext, mUserId); + } + private static final class PrintClient extends IPrintClient.Stub { private final WeakReference<PrintManager> mWeakPrintManager; diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java new file mode 100644 index 0000000..8fbdd9c --- /dev/null +++ b/core/java/android/print/PrinterDiscoverySession.java @@ -0,0 +1,297 @@ +/* + * 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.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +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; + +/** + * @hide + */ +public final class PrinterDiscoverySession { + + private static final String LOG_TAG ="PrinterDiscoverySession"; + + private static final int MSG_PRINTERS_ADDED = 1; + private static final int MSG_PRINTERS_REMOVED = 2; + private static final int MSG_PRINTERS_UPDATED = 3; + + private final ArrayMap<PrinterId, PrinterInfo> mPrinters = + new ArrayMap<PrinterId, PrinterInfo>(); + + private final IPrintManager mPrintManager; + + private final int mUserId; + + private final Handler mHandler; + + private IPrinterDiscoveryObserver mObserver; + + private OnPrintersChangeListener mListener; + + private boolean mIsPrinterDiscoveryStarted; + + public static interface OnPrintersChangeListener { + public void onPrintersChanged(); + } + + PrinterDiscoverySession(IPrintManager printManager, Context context, int userId) { + mPrintManager = printManager; + mUserId = userId; + mHandler = new SessionHandler(context.getMainLooper()); + mObserver = new PrinterDiscoveryObserver(this); + try { + mPrintManager.createPrinterDiscoverySession(mObserver, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error creating printer discovery session", re); + } + } + + public final void startPrinterDisovery(List<PrinterId> priorityList) { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring start printers dsicovery - session destroyed"); + } + if (!mIsPrinterDiscoveryStarted) { + mIsPrinterDiscoveryStarted = true; + try { + mPrintManager.startPrinterDiscovery(mObserver, priorityList, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error starting printer discovery", re); + } + } + } + + public final void stopPrinterDiscovery() { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring stop printers discovery - session destroyed"); + } + if (mIsPrinterDiscoveryStarted) { + mIsPrinterDiscoveryStarted = false; + try { + mPrintManager.stopPrinterDiscovery(mObserver, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error stopping printer discovery", re); + } + } + } + + public final void requestPrinterUpdate(PrinterId printerId) { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring reqeust printer update - session destroyed"); + } + try { + mPrintManager.requestPrinterUpdate(printerId, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error requesting printer update", re); + } + } + + public final void destroy() { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring destroy - session destroyed"); + } + destroyNoCheck(); + } + + public final List<PrinterInfo> getPrinters() { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring get printers - session destroyed"); + return Collections.emptyList(); + } + return new ArrayList<PrinterInfo>(mPrinters.values()); + } + + public final boolean isDestroyed() { + throwIfNotCalledOnMainThread(); + return isDestroyedNoCheck(); + } + + public final boolean isPrinterDiscoveryStarted() { + throwIfNotCalledOnMainThread(); + return mIsPrinterDiscoveryStarted; + } + + public final void setOnPrintersChangeListener(OnPrintersChangeListener listener) { + throwIfNotCalledOnMainThread(); + mListener = listener; + } + + @Override + protected final void finalize() throws Throwable { + if (!isDestroyedNoCheck()) { + Log.e(LOG_TAG, "Destroying leaked printer discovery session"); + destroyNoCheck(); + } + super.finalize(); + } + + private boolean isDestroyedNoCheck() { + return (mObserver == null); + } + + private void destroyNoCheck() { + stopPrinterDiscovery(); + try { + mPrintManager.destroyPrinterDiscoverySession(mObserver, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error destroying printer discovery session", re); + } finally { + mObserver = null; + mPrinters.clear(); + } + } + + private void handlePrintersAdded(List<PrinterInfo> printers) { + if (isDestroyed()) { + return; + } + boolean printersChanged = false; + 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); + printersChanged = true; + } + } + if (printersChanged) { + notifyOnPrintersChanged(); + } + } + + private void handlePrintersRemoved(List<PrinterId> printerIds) { + if (isDestroyed()) { + return; + } + boolean printersChanged = false; + final int removedPrinterIdCount = printerIds.size(); + for (int i = 0; i < removedPrinterIdCount; i++) { + PrinterId removedPrinterId = printerIds.get(i); + if (mPrinters.remove(removedPrinterId) != null) { + printersChanged = true; + } + } + if (printersChanged) { + notifyOnPrintersChanged(); + } + } + + private void handlePrintersUpdated(List<PrinterInfo> printers) { + if (isDestroyed()) { + return; + } + boolean printersChanged = false; + 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); + printersChanged = true; + } + } + if (printersChanged) { + notifyOnPrintersChanged(); + } + } + + private void notifyOnPrintersChanged() { + if (mListener != null) { + mListener.onPrintersChanged(); + } + } + + private static void throwIfNotCalledOnMainThread() { + if (!Looper.getMainLooper().isCurrentThread()) { + throw new IllegalAccessError("must be called from the main thread"); + } + } + + private final class SessionHandler extends Handler { + + public SessionHandler(Looper looper) { + super(looper, null, false); + } + + @Override + @SuppressWarnings("unchecked") + public void handleMessage(Message message) { + switch (message.what) { + case MSG_PRINTERS_ADDED: { + List<PrinterInfo> printers = (List<PrinterInfo>) message.obj; + handlePrintersAdded(printers); + } break; + + case MSG_PRINTERS_REMOVED: { + List<PrinterId> printerIds = (List<PrinterId>) message.obj; + handlePrintersRemoved(printerIds); + } break; + + case MSG_PRINTERS_UPDATED: { + List<PrinterInfo> printers = (List<PrinterInfo>) message.obj; + handlePrintersUpdated(printers); + } break; + } + } + } + + private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { + + private final WeakReference<PrinterDiscoverySession> mWeakSession; + + public PrinterDiscoveryObserver(PrinterDiscoverySession session) { + mWeakSession = new WeakReference<PrinterDiscoverySession>(session); + } + + @Override + public void onPrintersAdded(List<PrinterInfo> printers) { + PrinterDiscoverySession session = mWeakSession.get(); + if (session != null) { + session.mHandler.obtainMessage(MSG_PRINTERS_ADDED, + printers).sendToTarget(); + } + } + + @Override + public void onPrintersRemoved(List<PrinterId> printerIds) { + PrinterDiscoverySession session = mWeakSession.get(); + if (session != null) { + session.mHandler.obtainMessage(MSG_PRINTERS_REMOVED, + printerIds).sendToTarget(); + } + } + + @Override + public void onPrintersUpdated(List<PrinterInfo> printers) { + PrinterDiscoverySession session = mWeakSession.get(); + if (session != null) { + session.mHandler.obtainMessage(MSG_PRINTERS_UPDATED, + printers).sendToTarget(); + } + } + } +} diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml index 1f10af8..ab7ea09 100644 --- a/packages/PrintSpooler/AndroidManifest.xml +++ b/packages/PrintSpooler/AndroidManifest.xml @@ -18,12 +18,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.printspooler" - android:sharedUserId="android.uid.system" + android:sharedUserId="android.uid.printspooler" android:versionName="1" android:versionCode="1" coreApp="true"> - <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17"/> + <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/> <uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> @@ -36,7 +36,8 @@ <application android:allowClearUserData="false" android:label="@string/app_label" - android:allowBackup= "false"> + android:allowBackup= "false" + android:supportsRtl="true"> <service android:name=".PrintSpoolerService" @@ -46,6 +47,7 @@ <activity android:name=".PrintJobConfigActivity" + android:configChanges="orientation|screenSize" android:exported="false" android:theme="@style/PrintJobConfigActivityTheme"> </activity> diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml index 84c41de..252dd81 100644 --- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml +++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml @@ -14,208 +14,234 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/content_editing" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="vertical"> + android:orientation="vertical" + android:scrollbars="vertical" + android:background="@color/editable_background"> - <ScrollView + <GridLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - android:scrollbars="vertical" - android:background="@color/editable_background"> + android:columnCount="2"> - <GridLayout + <!-- Destination --> + + <Spinner + android:id="@+id/destination_spinner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_gravity="fill_horizontal" + android:layout_marginTop="24dip" + android:layout_marginStart="24dip" + android:layout_marginEnd="24dip" + android:layout_row="0" + android:layout_column="0" + android:layout_columnSpan="2" + android:minHeight="?android:attr/listPreferredItemHeightSmall"> + </Spinner> + + <!-- Copies --> + + <view + class="com.android.printspooler.PrintJobConfigActivity$CustomEditText" + android:id="@+id/copies_edittext" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginStart="24dip" + android:layout_marginEnd="6dip" + android:layout_row="2" + android:layout_column="0" + android:layout_gravity="bottom|fill_horizontal" + style="@style/PrintOptionEditTextStyle" + android:inputType="numberDecimal"> + </view> + + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_marginStart="36dip" + android:layout_marginEnd="6dip" + android:layout_row="1" + android:layout_column="0" + android:layout_gravity="start|bottom" + style="@style/PrintOptionTextViewStyle" + android:labelFor="@id/copies_edittext" + android:text="@string/label_copies"> + </TextView> + + <!-- Paper size --> + + <Spinner + android:id="@+id/paper_size_spinner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginStart="6dip" + android:layout_marginEnd="24dip" + android:layout_row="2" + android:layout_column="1" + android:layout_gravity="fill_horizontal" + style="@style/PrintOptionSpinnerStyle"> + </Spinner> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_marginStart="18dip" + android:layout_marginEnd="24dip" + android:layout_row="1" + android:layout_column="1" + style="@style/PrintOptionTextViewStyle" + android:labelFor="@id/paper_size_spinner" + android:text="@string/label_paper_size"> + </TextView> + + <!-- Color --> + + <Spinner + android:id="@+id/color_spinner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" android:layout_marginStart="24dip" - android:layout_marginTop="32dip" + android:layout_marginEnd="6dip" + android:layout_row="4" + android:layout_column="0" + android:layout_gravity="fill_horizontal" + style="@style/PrintOptionSpinnerStyle"> + </Spinner> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_marginStart="36dip" + android:layout_marginEnd="6dip" + android:layout_row="3" + android:layout_column="0" + style="@style/PrintOptionTextViewStyle" + android:labelFor="@id/color_spinner" + android:text="@string/label_color"> + </TextView> + + <!-- Orientation --> + + <Spinner + android:id="@+id/orientation_spinner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginStart="6dip" android:layout_marginEnd="24dip" - android:layout_marginBottom="24dip" - android:orientation="vertical" - android:columnCount="2"> - - <!-- Destination --> - - <Spinner - android:id="@+id/destination_spinner" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_gravity="fill_horizontal" - android:layout_row="0" - android:layout_column="0" - android:layout_columnSpan="2" - android:minHeight="?android:attr/listPreferredItemHeightSmall"> - </Spinner> - - <!-- Copies --> - - <view - class="com.android.printspooler.PrintJobConfigActivity$CustomEditText" - android:id="@+id/copies_edittext" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:layout_row="2" - android:layout_column="0" - android:layout_gravity="bottom" - android:inputType="numberDecimal" - android:selectAllOnFocus="true" - android:minWidth="150dip" - android:minHeight="?android:attr/listPreferredItemHeightSmall"> - </view> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="12dip" - android:layout_marginStart="12dip" - android:layout_marginEnd="6dip" - android:layout_row="1" - android:layout_column="0" - android:layout_gravity="start|bottom" - android:labelFor="@id/copies_edittext" - android:text="@string/label_copies" - android:textAppearance="@style/PrintOptionTitleTextAppearance"> - </TextView> - - <!-- Paper size --> - - <Spinner - android:id="@+id/paper_size_spinner" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginStart="6dip" - android:layout_row="2" - android:layout_column="1" - style="@style/PrintOptionSpinnerStyle"> - </Spinner> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="12dip" - android:layout_marginStart="18dip" - android:layout_row="1" - android:layout_column="1" - android:labelFor="@id/paper_size_spinner" - android:text="@string/label_paper_size" - android:textAppearance="@style/PrintOptionTitleTextAppearance"> - </TextView> - - <!-- Color --> - - <Spinner - android:id="@+id/color_spinner" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:layout_row="4" - android:layout_column="0" - style="@style/PrintOptionSpinnerStyle"> - </Spinner> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="12dip" - android:layout_marginStart="12dip" - android:layout_marginEnd="6dip" - android:layout_row="3" - android:layout_column="0" - android:labelFor="@id/color_spinner" - android:text="@string/label_color" - android:textAppearance="@style/PrintOptionTitleTextAppearance"> - </TextView> - - <!-- Orientation --> - - <Spinner - android:id="@+id/orientation_spinner" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginStart="6dip" - android:layout_row="4" - android:layout_column="1" - style="@style/PrintOptionSpinnerStyle"> - </Spinner> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="12dip" - android:layout_marginStart="18dip" - android:layout_row="3" - android:layout_column="1" - android:labelFor="@id/orientation_spinner" - android:text="@string/label_orientation" - android:textAppearance="@style/PrintOptionTitleTextAppearance"> - </TextView> - - <!-- Pages --> - - <Spinner - android:id="@+id/range_options_spinner" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="6dip" - android:layout_row="6" - android:layout_column="0" - style="@style/PrintOptionSpinnerStyle"> - </Spinner> - - <view - class="com.android.printspooler.PrintJobConfigActivity$CustomEditText" - android:id="@+id/page_range_edittext" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="12dip" - android:layout_marginStart="6dip" - android:layout_row="6" - android:layout_column="1" - android:layout_gravity="bottom" - android:selectAllOnFocus="true" - android:minWidth="150dip" - android:hint="@string/pages_range_example" - android:inputType="textNoSuggestions" - android:visibility="gone" - android:minHeight="?android:attr/listPreferredItemHeightSmall"> - </view> - - <TextView - android:id="@+id/page_range_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="12dip" - android:layout_marginStart="12dip" - android:layout_row="5" - android:layout_column="0" - android:labelFor="@id/range_options_spinner" - android:text="@string/label_pages" - android:textAppearance="@style/PrintOptionTitleTextAppearance"> - </TextView> - - </GridLayout> - - </ScrollView> - - <View - android:layout_width="fill_parent" - android:layout_height="1dip" - android:background="@color/separator"> - </View> - - <Button - android:id="@+id/print_button" - style="?android:attr/buttonBarButtonStyle" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_gravity="fill_horizontal" - android:text="@string/print_button" - android:textSize="16sp" - android:textColor="@color/important_text"> - </Button> + android:layout_row="4" + android:layout_column="1" + android:layout_gravity="fill_horizontal" + style="@style/PrintOptionSpinnerStyle"> + </Spinner> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_marginStart="18dip" + android:layout_marginEnd="24dip" + android:layout_row="3" + android:layout_column="1" + style="@style/PrintOptionTextViewStyle" + android:labelFor="@id/orientation_spinner" + android:text="@string/label_orientation"> + </TextView> + + <!-- Range options --> + + <Spinner + android:id="@+id/range_options_spinner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginStart="24dip" + android:layout_marginEnd="6dip" + android:layout_row="6" + android:layout_column="0" + android:layout_gravity="fill_horizontal" + style="@style/PrintOptionSpinnerStyle"> + </Spinner> + + <TextView + android:id="@+id/range_options_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_marginStart="36dip" + android:layout_row="5" + android:layout_column="0" + style="@style/PrintOptionTextViewStyle" + android:labelFor="@id/range_options_spinner" + android:text="@string/label_pages"> + </TextView> + + <!-- Pages --> + + <view + class="com.android.printspooler.PrintJobConfigActivity$CustomEditText" + android:id="@+id/page_range_edittext" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginStart="6dip" + android:layout_marginEnd="24dip" + android:layout_row="6" + android:layout_column="1" + android:layout_gravity="bottom|fill_horizontal" + style="@style/PrintOptionEditTextStyle" + android:visibility="gone" + android:inputType="textNoSuggestions"> + </view> + + <TextView + android:id="@+id/page_range_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_marginStart="12dip" + android:layout_marginEnd="24dip" + android:layout_row="5" + android:layout_column="1" + style="@style/PrintOptionTextViewStyle" + android:labelFor="@id/page_range_edittext" + android:text="@string/pages_range_example" + android:textAllCaps="false"> + </TextView> + + <!-- Print button --> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="1dip" + android:layout_marginTop="24dip" + android:layout_row="7" + android:layout_column="0" + android:layout_columnSpan="2" + android:layout_gravity="fill_horizontal" + android:background="@color/separator" + android:contentDescription="@null"> + </ImageView> + + <Button + android:id="@+id/print_button" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="fill_horizontal" + android:layout_row="8" + android:layout_column="0" + android:layout_columnSpan="2" + android:text="@string/print_button" + android:textSize="16sp" + android:textColor="@color/important_text"> + </Button> + + </GridLayout> + +</ScrollView> -</LinearLayout> diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index 41fc516..ee3cf84 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -22,6 +22,9 @@ <!-- Label of the print dialog's print button. [CHAR LIMIT=16] --> <string name="print_button">Print</string> + <!-- Label of the print dialog's save button. [CHAR LIMIT=16] --> + <string name="save_button">Save</string> + <!-- Label of the destination widget. [CHAR LIMIT=20] --> <string name="label_destination">DESTIINATION</string> @@ -41,7 +44,7 @@ <string name="label_pages">PAGES (<xliff:g id="page_count" example="5">%1$s</xliff:g>)</string> <!-- Page range exmple used as a hint of how to specify such. [CHAR LIMIT=15] --> - <string name="pages_range_example">e.g. 1–5, 8</string> + <string name="pages_range_example">e.g. 1–5, 8, 11–13</string> <!-- Title for the pring preview button .[CHAR LIMIT=30] --> <string name="print_preview">Print preview</string> @@ -62,11 +65,7 @@ <string name="save_as_pdf">Save as PDF</string> <!-- Title for the open all printers UI option in the printer list. [CHAR LIMIT=30] --> - <string name="all_printers">All printers\.\.\.</string> - - <!-- Title for the searching for printers option in the printer list - (only option if not printers are available). [CHAR LIMIT=40] --> - <string name="searching_for_printers">Searching for printers\.\.\.</string> + <string name="all_printers">All printers…</string> <!-- Select printer activity --> diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml index 702adf4..fe11c93 100644 --- a/packages/PrintSpooler/res/values/styles.xml +++ b/packages/PrintSpooler/res/values/styles.xml @@ -27,7 +27,21 @@ <item name="android:paddingTop">0dip</item> <item name="android:paddingBottom">0dip</item> <item name="android:minWidth">150dip</item> + <item name="android:maxWidth">200dip</item> <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item> </style> + <style name="PrintOptionEditTextStyle"> + <item name="android:selectAllOnFocus">true</item> + <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="android:maxWidth">200dip</item> + <item name="android:singleLine">true</item> + <item name="android:ellipsize">end</item> + </style> + + <style name="PrintOptionTextViewStyle"> + <item name="android:textAppearance">@style/PrintOptionTitleTextAppearance</item> + <item name="android:maxWidth">200dip</item> + </style> + </resources> diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml index 831b0ec..bb41527 100644 --- a/packages/PrintSpooler/res/values/themes.xml +++ b/packages/PrintSpooler/res/values/themes.xml @@ -18,7 +18,7 @@ <style name="PrintJobConfigActivityTheme" parent="@android:style/Theme.Holo.Light.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> - <item name="android:windowSoftInputMode">stateAlwaysHidden|adjustPan</item> + <item name="android:windowSoftInputMode">stateAlwaysHidden|adjustResize</item> <item name="android:windowIsTranslucent">true</item> <item name="android:backgroundDimEnabled">true</item> <item name="android:colorBackgroundCacheHint">@android:color/transparent</item> diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java index 6bad5b3..28fd0e0 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java @@ -21,6 +21,9 @@ import android.content.Context; import android.content.Loader; import android.os.AsyncTask; import android.os.Build; +import android.print.PrintManager; +import android.print.PrinterDiscoverySession; +import android.print.PrinterDiscoverySession.OnPrintersChangeListener; import android.print.PrinterId; import android.print.PrinterInfo; import android.util.ArrayMap; @@ -30,7 +33,6 @@ import android.util.Slog; import android.util.Xml; import com.android.internal.util.FastXmlSerializer; -import com.android.printspooler.PrintSpoolerService.PrinterDiscoverySession; import libcore.io.IoUtils; @@ -62,7 +64,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { private static final int MAX_HISTORY_LENGTH = 50; - private static final int MAX_HISTORICAL_PRINTER_COUNT = 4; + private static final int MAX_FAVORITE_PRINTER_COUNT = 4; private final Map<PrinterId, PrinterInfo> mPrinters = new LinkedHashMap<PrinterId, PrinterInfo>(); @@ -96,7 +98,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected void onStartLoading() { if (DEBUG) { - Log.i(LOG_TAG, "onStartLoading()"); + Log.i(LOG_TAG, "onStartLoading()" + FusedPrintersProvider.this.hashCode()); } // The contract is that if we already have a valid, // result the we have to deliver it immediately. @@ -113,7 +115,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected void onStopLoading() { if (DEBUG) { - Log.i(LOG_TAG, "onStopLoading()"); + Log.i(LOG_TAG, "onStopLoading()" + FusedPrintersProvider.this.hashCode()); } onCancelLoad(); } @@ -121,7 +123,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected void onForceLoad() { if (DEBUG) { - Log.i(LOG_TAG, "onForceLoad()"); + Log.i(LOG_TAG, "onForceLoad()" + FusedPrintersProvider.this.hashCode()); } onCancelLoad(); loadInternal(); @@ -129,12 +131,21 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { private void loadInternal() { if (mDiscoverySession == null) { - mDiscoverySession = new MyPrinterDiscoverySession(); + PrintManager printManager = (PrintManager) getContext() + .getSystemService(Context.PRINT_SERVICE); + mDiscoverySession = printManager.createPrinterDiscoverySession(); + mDiscoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() { + @Override + public void onPrintersChanged() { + deliverResult(new ArrayList<PrinterInfo>( + mDiscoverySession.getPrinters())); + } + }); mPersistenceManager.readPrinterHistory(); } if (mPersistenceManager.isReadHistoryCompleted() - && !mDiscoverySession.isStarted()) { - final int favoriteCount = Math.min(MAX_HISTORICAL_PRINTER_COUNT, + && !mDiscoverySession.isPrinterDiscoveryStarted()) { + final int favoriteCount = Math.min(MAX_FAVORITE_PRINTER_COUNT, mFavoritePrinters.size()); List<PrinterId> printerIds = new ArrayList<PrinterId>(favoriteCount); for (int i = 0; i < favoriteCount; i++) { @@ -147,13 +158,14 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected boolean onCancelLoad() { if (DEBUG) { - Log.i(LOG_TAG, "onCancelLoad()"); + Log.i(LOG_TAG, "onCancelLoad()" + FusedPrintersProvider.this.hashCode()); } return cancelInternal(); } private boolean cancelInternal() { - if (mDiscoverySession != null && mDiscoverySession.isStarted()) { + if (mDiscoverySession != null + && mDiscoverySession.isPrinterDiscoveryStarted()) { mDiscoverySession.stopPrinterDiscovery(); return true; } else if (mPersistenceManager.isReadHistoryInProgress()) { @@ -165,7 +177,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected void onReset() { if (DEBUG) { - Log.i(LOG_TAG, "onReset()"); + Log.i(LOG_TAG, "onReset()" + FusedPrintersProvider.this.hashCode()); } onStopLoading(); mPrinters.clear(); @@ -178,73 +190,15 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected void onAbandon() { if (DEBUG) { - Log.i(LOG_TAG, "onAbandon()"); + Log.i(LOG_TAG, "onAbandon()" + FusedPrintersProvider.this.hashCode()); } onStopLoading(); } public void refreshPrinter(PrinterId printerId) { - if (isStarted() && mDiscoverySession != null && mDiscoverySession.isStarted()) { - mDiscoverySession.requestPrinterUpdated(printerId); - } - } - - private final class MyPrinterDiscoverySession extends PrinterDiscoverySession { - - @Override - public void onPrintersAdded(List<PrinterInfo> printers) { - if (DEBUG) { - Log.i(LOG_TAG, "MyPrinterDiscoverySession#onPrintersAdded()"); - } - boolean printersAdded = false; - final int addedPrinterCount = printers.size(); - for (int i = 0; i < addedPrinterCount; i++) { - PrinterInfo printer = printers.get(i); - if (!mPrinters.containsKey(printer.getId())) { - mPrinters.put(printer.getId(), printer); - printersAdded = true; - } - } - if (printersAdded) { - deliverResult(new ArrayList<PrinterInfo>(mPrinters.values())); - } - } - - @Override - public void onPrintersRemoved(List<PrinterId> printerIds) { - if (DEBUG) { - Log.i(LOG_TAG, "MyPrinterDiscoverySession#onPrintersRemoved()"); - } - boolean removedPrinters = false; - final int removedPrinterCount = printerIds.size(); - for (int i = 0; i < removedPrinterCount; i++) { - PrinterId removedPrinterId = printerIds.get(i); - if (mPrinters.remove(removedPrinterId) != null) { - removedPrinters = true; - } - } - if (removedPrinters) { - deliverResult(new ArrayList<PrinterInfo>(mPrinters.values())); - } - } - - @Override - public void onPrintersUpdated(List<PrinterInfo> printers) { - if (DEBUG) { - Log.i(LOG_TAG, "MyPrinterDiscoverySession#onPrintersUpdated()"); - } - boolean updatedPrinters = false; - final int updatedPrinterCount = printers.size(); - for (int i = 0; i < updatedPrinterCount; i++) { - PrinterInfo updatedPrinter = printers.get(i); - if (mPrinters.containsKey(updatedPrinter.getId())) { - mPrinters.put(updatedPrinter.getId(), updatedPrinter); - updatedPrinters = true; - } - } - if (updatedPrinters) { - deliverResult(new ArrayList<PrinterInfo>(mPrinters.values())); - } + if (isStarted() && mDiscoverySession != null + && mDiscoverySession.isPrinterDiscoveryStarted()) { + mDiscoverySession.requestPrinterUpdate(printerId); } } @@ -280,7 +234,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { @Override protected void onPostExecute(List<PrinterInfo> printers) { if (DEBUG) { - Log.i(LOG_TAG, "read history completed"); + Log.i(LOG_TAG, "read history completed" + FusedPrintersProvider.this.hashCode()); } mHistoricalPrinters = printers; @@ -290,7 +244,7 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { // We want the first few favorite printers on top of the list. final int favoriteCount = Math.min(mFavoritePrinters.size(), - MAX_HISTORICAL_PRINTER_COUNT); + MAX_FAVORITE_PRINTER_COUNT); for (int i = 0; i < favoriteCount; i++) { PrinterInfo favoritePrinter = mFavoritePrinters.get(i); mPrinters.put(favoritePrinter.getId(), favoritePrinter); @@ -299,6 +253,10 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { mReadHistoryInProgress = false; mReadHistoryCompleted = true; + // Deliver the favorites. + deliverResult(mFavoritePrinters); + + // Start loading the available printers. loadInternal(); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index d3dd8c9..c01d8f8 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -64,7 +64,6 @@ import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; @@ -111,15 +110,10 @@ public class PrintJobConfigActivity extends Activity { 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; @@ -196,15 +190,11 @@ public class PrintJobConfigActivity extends Activity { 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), PrintSpoolerService.peekInstance().generateFileForPrintJob(mPrintJobId))); + mEditor = new Editor(); try { mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0); @@ -218,6 +208,12 @@ public class PrintJobConfigActivity extends Activity { } @Override + public void onResume() { + super.onResume(); + mEditor.refreshCurrentPrinter(); + } + + @Override protected void onDestroy() { // We can safely do the work in here since at this point // the system is bound to our (spooler) process which @@ -294,6 +290,8 @@ public class PrintJobConfigActivity extends Activity { private int mControllerState = CONTROLLER_STATE_INITIALIZED; + private boolean mHasStarted; + private PageRange[] mRequestedPages; public PrintController(RemotePrintDocumentAdapter adapter) { @@ -305,6 +303,7 @@ public class PrintJobConfigActivity extends Activity { } public void initialize() { + mHasStarted = false; mControllerState = CONTROLLER_STATE_INITIALIZED; } @@ -321,7 +320,7 @@ public class PrintJobConfigActivity extends Activity { } public boolean hasStarted() { - return mControllerState >= CONTROLLER_STATE_STARTED; + return mHasStarted; } public boolean hasPerformedLayout() { @@ -335,10 +334,14 @@ public class PrintJobConfigActivity extends Activity { public void start() { mControllerState = CONTROLLER_STATE_STARTED; + mHasStarted = true; mRemotePrintAdapter.start(); } public void update() { + if (!mController.hasStarted()) { + mController.start(); + } if (!printAttributesChanged()) { // 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 @@ -372,6 +375,7 @@ public class PrintJobConfigActivity extends Activity { } if (isCancelled()) { + mEditor.updateUi(); if (mEditor.isDone()) { PrintJobConfigActivity.this.finish(); } @@ -379,7 +383,6 @@ public class PrintJobConfigActivity extends Activity { } mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED; - mEditor.updateUi(); // If the info changed, we update the document and the print job. final boolean infoChanged = !info.equals(mDocument.info); @@ -402,6 +405,7 @@ public class PrintJobConfigActivity extends Activity { // trigger an update. mRequestedPages = mEditor.getRequestedPages(); if (mRequestedPages == null) { + mEditor.updateUi(); if (mEditor.isDone()) { PrintJobConfigActivity.this.finish(); } @@ -423,12 +427,15 @@ public class PrintJobConfigActivity extends Activity { // preview button, then just skip the write. if (!LIVE_PREVIEW_SUPPORTED && !mEditor.isPreviewConfirmed() && mMetadata.getBoolean(PrintDocumentAdapter.METADATA_KEY_PRINT_PREVIEW)) { + mEditor.updateUi(); if (mEditor.isDone()) { PrintJobConfigActivity.this.finish(); } return; } + mEditor.updateUi(); + // Request a write of the pages of interest. mControllerState = CONTROLLER_STATE_WRITE_STARTED; mRemotePrintAdapter.write(mRequestedPages, mWriteResultCallback, @@ -647,7 +654,6 @@ public class PrintJobConfigActivity extends Activity { 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; @@ -704,8 +710,9 @@ public class PrintJobConfigActivity extends Activity { private EditText mCopiesEditText; - private TextView mRangeTitle; - private EditText mRangeEditText; + private TextView mRangeOptionsTitle; + private TextView mPageRangeTitle; + private EditText mPageRangeEditText; private Spinner mDestinationSpinner; private final DestinationAdapter mDestinationSpinnerAdapter; @@ -729,6 +736,8 @@ public class PrintJobConfigActivity extends Activity { private Button mPrintButton; + private PrinterInfo mCurrentPrinter; + private final OnItemSelectedListener mOnItemSelectedListener = new AdapterView.OnItemSelectedListener() { @Override @@ -738,69 +747,37 @@ 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); + startSelectPrinterActivity(); return; } - mWaitingForPrinterCapabilities = false; + mCurrPrintAttributes.clear(); + PrinterInfo printer = (PrinterInfo) mDestinationSpinnerAdapter .getItem(position); + + PrintSpoolerService.peekInstance().setPrintJobPrinterNoPersistence( + mPrintJobId, printer); + if (printer != null) { - PrintSpoolerService.peekInstance().setPrintJobPrinterNoPersistence( - mPrintJobId, printer); PrinterCapabilitiesInfo capabilities = printer.getCapabilities(); if (capabilities == null) { - List<PrinterId> printerIds = new ArrayList<PrinterId>(); - printerIds.add(printer.getId()); - 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. + mEditor.refreshCurrentPrinter(); } else { capabilities.getDefaults(mCurrPrintAttributes); if (!mController.hasStarted()) { mController.start(); } - if (!hasErrors()) { - mController.update(); - } + mController.update(); } } - // 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); - } + mCurrentPrinter = printer; - updateUi(); + updateUiForNewPrinterCapabilities(); } else if (spinner == mMediaSizeSpinner) { if (mIgnoreNextMediaSizeChange) { mIgnoreNextMediaSizeChange = false; @@ -877,7 +854,13 @@ public class PrintJobConfigActivity extends Activity { return; } - final int copies = Integer.parseInt(editable.toString()); + int copies = 0; + try { + copies = Integer.parseInt(editable.toString()); + } catch (NumberFormatException nfe) { + /* ignore */ + } + if (copies < MIN_COPIES) { mCopiesEditText.setError(""); updateUi(); @@ -918,14 +901,14 @@ public class PrintJobConfigActivity extends Activity { String text = editable.toString(); if (TextUtils.isEmpty(text)) { - mRangeEditText.setError(""); + mPageRangeEditText.setError(""); updateUi(); return; } String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////"); if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) { - mRangeEditText.setError(""); + mPageRangeEditText.setError(""); updateUi(); return; } @@ -935,7 +918,7 @@ public class PrintJobConfigActivity extends Activity { String numericString = text.substring(matcher.start(), matcher.end()); final int pageIndex = Integer.parseInt(numericString); if (pageIndex < 1 || pageIndex > mDocument.info.getPageCount()) { - mRangeEditText.setError(""); + mPageRangeEditText.setError(""); updateUi(); return; } @@ -943,7 +926,7 @@ public class PrintJobConfigActivity extends Activity { //TODO: Catch the error if start is less grater than the end. - mRangeEditText.setError(null); + mPageRangeEditText.setError(null); mPrintButton.setEnabled(true); updateUi(); @@ -963,45 +946,70 @@ public class PrintJobConfigActivity extends Activity { private boolean mIgnoreNextCopiesChange; private boolean mIgnoreNextRangeChange; - private boolean mWaitingForPrinterCapabilities; - private int mCurrentUi = UI_NONE; + private boolean mFavoritePrinterSelected; + public Editor() { // Destination. 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); + // Initially, we have only sage to PDF as a printer but after some + // printers are loaded we want to select the user's favorite one + // which is the first. + if (!mFavoritePrinterSelected && mDestinationSpinnerAdapter.getCount() > 2) { + mFavoritePrinterSelected = true; + mDestinationSpinner.setSelection(0); } - // 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 - // care of updating everything. This way the logic is in one place. - if (mWaitingForPrinterCapabilities) { - mWaitingForPrinterCapabilities = false; - PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem(); - if (printer != null && printer.getCapabilities() != null) { - mOnItemSelectedListener.onItemSelected(mDestinationSpinner, null, - selectedPosition, selectedPosition); + // If the current printer properties changed, we update the UI. + if (mCurrentPrinter != null) { + final int printerCount = mDestinationSpinnerAdapter.getCount(); + for (int i = 0; i < printerCount; i++) { + Object item = mDestinationSpinnerAdapter.getItem(i); + // Some items are not printers + if (item instanceof PrinterInfo) { + PrinterInfo printer = (PrinterInfo) item; + if (!printer.getId().equals(mCurrentPrinter.getId())) { + continue; + } + + // Update the UI if capabilities changed. + boolean capabilitiesChanged = false; + + if (mCurrentPrinter.getCapabilities() == null) { + if (printer.getCapabilities() != null) { + capabilitiesChanged = true; + } + } else if (!mCurrentPrinter.getCapabilities().equals( + printer.getCapabilities())) { + capabilitiesChanged = true; + } + + if (capabilitiesChanged) { + // Update the current printer. + mCurrentPrinter.copyFrom(printer); + + // If something changed during UI update... + if (updateUi()) { + // Update current attributes. + printer.getCapabilities().getDefaults(mCurrPrintAttributes); + // Update the document. + mController.update(); + } + } + + break; + } } } - updateUi(); } @Override public void onInvalidated() { - updateUi(); + updateUiForNewPrinterCapabilities(); } }); @@ -1039,17 +1047,24 @@ public class PrintJobConfigActivity extends Activity { 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 void refreshCurrentPrinter() { + PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem(); + if (printer != null) { + FusedPrintersProvider printersLoader = (FusedPrintersProvider) + (Loader<?>) getLoaderManager().getLoader( + LOADER_ID_PRINTERS_LOADER); + if (printersLoader != null) { + printersLoader.refreshPrinter(printer.getId()); } } } + public void selectPrinter(PrinterId printerId) { + mDestinationSpinnerAdapter.ensurePrinterShownPrinterShown(printerId); + final int position = mDestinationSpinnerAdapter.getPrinterIndex(printerId); + mDestinationSpinner.setSelection(position); + } + public boolean isPrintingToPdf() { return mDestinationSpinner.getSelectedItem() == mDestinationSpinnerAdapter.mFakePdfPrinter; @@ -1163,12 +1178,7 @@ public class PrintJobConfigActivity extends Activity { 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); - } + mEditor.refreshCurrentPrinter(); } } else { mEditor.cancel(); @@ -1288,7 +1298,7 @@ public class PrintJobConfigActivity extends Activity { } if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) { List<PageRange> pageRanges = new ArrayList<PageRange>(); - mStringCommaSplitter.setString(mRangeEditText.getText().toString()); + mStringCommaSplitter.setString(mPageRangeEditText.getText().toString()); while (mStringCommaSplitter.hasNext()) { String range = mStringCommaSplitter.next().trim(); @@ -1340,7 +1350,7 @@ public class PrintJobConfigActivity extends Activity { mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner); mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter); mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - if (mDestinationSpinnerAdapter.getCount() > 0) { + if (mDestinationSpinnerAdapter.getCount() > 0 && mController.hasStarted()) { mIgnoreNextDestinationChange = true; } @@ -1368,12 +1378,8 @@ public class PrintJobConfigActivity extends Activity { mIgnoreNextOrientationChange = true; } - // Range - mRangeTitle = (TextView) findViewById(R.id.page_range_title); - mRangeEditText = (EditText) findViewById(R.id.page_range_edittext); - mRangeEditText.addTextChangedListener(mRangeTextWatcher); - // Range options + mRangeOptionsTitle = (TextView) findViewById(R.id.range_options_title); mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner); mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter); mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener); @@ -1381,14 +1387,19 @@ public class PrintJobConfigActivity extends Activity { mIgnoreNextRangeOptionChange = true; } + // Page range + mPageRangeTitle = (TextView) findViewById(R.id.page_range_title); + mPageRangeEditText = (EditText) findViewById(R.id.page_range_edittext); + mPageRangeEditText.addTextChangedListener(mRangeTextWatcher); + // Print button mPrintButton = (Button) findViewById(R.id.print_button); registerPrintButtonClickListener(); } - public void updateUi() { + public boolean updateUi() { if (mCurrentUi != UI_EDITING_PRINT_JOB) { - return; + return false; } if (isPrintConfirmed() || isPreviewConfirmed() || isCancelled()) { mDestinationSpinner.setEnabled(false); @@ -1397,11 +1408,11 @@ public class PrintJobConfigActivity extends Activity { mColorModeSpinner.setEnabled(false); mOrientationSpinner.setEnabled(false); mRangeOptionsSpinner.setEnabled(false); - mRangeEditText.setEnabled(false); + mPageRangeEditText.setEnabled(false); // TODO: Remove entirely or implement print preview. // mPrintPreviewButton.setEnabled(false); mPrintButton.setEnabled(false); - return; + return false; } // If a printer with capabilities is selected, then we enabled all options. @@ -1452,15 +1463,16 @@ public class PrintJobConfigActivity extends Activity { mRangeOptionsSpinner.setSelection(0); } mRangeOptionsSpinner.setEnabled(false); - mRangeTitle.setText(getString(R.string.label_pages, + mRangeOptionsTitle.setText(getString(R.string.label_pages, getString(R.string.page_count_unknown))); - if (!TextUtils.equals(mRangeEditText.getText(), "")) { + if (!TextUtils.equals(mPageRangeEditText.getText(), "")) { mIgnoreNextRangeChange = true; - mRangeEditText.setText(""); + mPageRangeEditText.setText(""); } - mRangeEditText.setEnabled(false); - mRangeEditText.setVisibility(View.INVISIBLE); + mPageRangeEditText.setEnabled(false); + mPageRangeEditText.setVisibility(View.INVISIBLE); + mPageRangeTitle.setVisibility(View.INVISIBLE); // // Print preview // mPrintPreviewButton.setEnabled(false); @@ -1468,17 +1480,20 @@ public class PrintJobConfigActivity extends Activity { // Print mPrintButton.setEnabled(false); + + return false; } else { + boolean someAttributeSelectionChanged = false; + PrintAttributes defaultAttributes = mTempPrintAttributes; PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem(); PrinterCapabilitiesInfo capabilities = printer.getCapabilities(); printer.getCapabilities().getDefaults(defaultAttributes); - // Copies - mCopiesEditText.setEnabled(true); - // Media size. List<MediaSize> mediaSizes = capabilities.getMediaSizes(); + + // If the media sizes changed, we update the adapter and the spinner. boolean mediaSizesChanged = false; final int mediaSizeCount = mediaSizes.size(); if (mediaSizeCount != mMediaSizeSpinnerAdapter.getCount()) { @@ -1492,22 +1507,40 @@ public class PrintJobConfigActivity extends Activity { } } if (mediaSizesChanged) { + // Remember the old media size to try selecting it again. + int oldMediaSizeNewIndex = AdapterView.INVALID_POSITION; + MediaSize oldMediaSize = mCurrPrintAttributes.getMediaSize(); + + // Rebuild the adapter data. mMediaSizeSpinnerAdapter.clear(); for (int i = 0; i < mediaSizeCount; i++) { MediaSize mediaSize = mediaSizes.get(i); + if (mediaSize.equals(oldMediaSize)) { + // Update the index of the old selection. + oldMediaSizeNewIndex = i; + } mMediaSizeSpinnerAdapter.add(new SpinnerItem<MediaSize>( mediaSize, mediaSize.getLabel())); } + if (mediaSizeCount <= 0) { + // No media sizes - clear the selection. mMediaSizeSpinner.setEnabled(false); - mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION); + // Clear selection and mark if selection changed. + someAttributeSelectionChanged = setMediaSizeSpinnerSelectionNoCallback( + AdapterView.INVALID_POSITION); } else { mMediaSizeSpinner.setEnabled(true); - final int selectedMediaSizeIndex = Math.max(mediaSizes.indexOf( - defaultAttributes.getMediaSize()), 0); - if (mMediaSizeSpinner.getSelectedItemPosition() != selectedMediaSizeIndex) { - mIgnoreNextMediaSizeChange = true; - mMediaSizeSpinner.setSelection(selectedMediaSizeIndex); + + if (oldMediaSizeNewIndex != AdapterView.INVALID_POSITION) { + // Select the old media size - nothing really changed. + setMediaSizeSpinnerSelectionNoCallback(oldMediaSizeNewIndex); + } else { + // Select the first or the default and mark if selection changed. + final int mediaSizeIndex = Math.max(mediaSizes.indexOf( + defaultAttributes.getMediaSize()), 0); + someAttributeSelectionChanged = setMediaSizeSpinnerSelectionNoCallback( + mediaSizeIndex); } } } @@ -1619,20 +1652,22 @@ public class PrintJobConfigActivity extends Activity { || info.getPageCount() == PrintDocumentInfo.PAGE_COUNT_UNKNOWN)) { mRangeOptionsSpinner.setEnabled(true); if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) { - if (!mRangeEditText.isEnabled()) { - mRangeEditText.setEnabled(true); - mRangeEditText.setVisibility(View.VISIBLE); - mRangeEditText.requestFocus(); + if (!mPageRangeEditText.isEnabled()) { + mPageRangeEditText.setEnabled(true); + mPageRangeEditText.setVisibility(View.VISIBLE); + mPageRangeTitle.setVisibility(View.VISIBLE); + mPageRangeEditText.requestFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - imm.showSoftInput(mRangeEditText, 0); + imm.showSoftInput(mPageRangeEditText, 0); } } else { - mRangeEditText.setEnabled(false); - mRangeEditText.setVisibility(View.INVISIBLE); + mPageRangeEditText.setEnabled(false); + mPageRangeEditText.setVisibility(View.INVISIBLE); + mPageRangeTitle.setVisibility(View.INVISIBLE); } final int pageCount = mDocument.info.getPageCount(); - mRangeTitle.setText(getString(R.string.label_pages, + mRangeOptionsTitle.setText(getString(R.string.label_pages, (pageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN) ? getString(R.string.page_count_unknown) : String.valueOf(pageCount))); @@ -1642,16 +1677,29 @@ public class PrintJobConfigActivity extends Activity { mRangeOptionsSpinner.setSelection(0); } mRangeOptionsSpinner.setEnabled(false); - mRangeTitle.setText(getString(R.string.label_pages, + mRangeOptionsTitle.setText(getString(R.string.label_pages, getString(R.string.page_count_unknown))); - mRangeEditText.setEnabled(false); - mRangeEditText.setVisibility(View.INVISIBLE); + mPageRangeEditText.setEnabled(false); + mPageRangeEditText.setVisibility(View.INVISIBLE); + mPageRangeTitle.setVisibility(View.INVISIBLE); } mRangeOptionsSpinner.setEnabled(true); // Print/Print preview + if (mDestinationSpinner.getSelectedItemId() + != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) { + String newText = getString(R.string.print_button); + if (!TextUtils.equals(newText, mPrintButton.getText())) { + mPrintButton.setText(R.string.print_button); + } + } else { + String newText = getString(R.string.save_button); + if (!TextUtils.equals(newText, mPrintButton.getText())) { + mPrintButton.setText(R.string.save_button); + } + } if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1 - && (TextUtils.isEmpty(mRangeEditText.getText()) || hasErrors())) + && (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors())) || (mRangeOptionsSpinner.getSelectedItemPosition() == 0 && (!mController.hasPerformedLayout() || hasErrors()))) { // mPrintPreviewButton.setEnabled(false); @@ -1667,6 +1715,12 @@ public class PrintJobConfigActivity extends Activity { } // Copies + if (mDestinationSpinner.getSelectedItemId() + != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) { + mCopiesEditText.setEnabled(true); + } else { + mCopiesEditText.setEnabled(false); + } if (mCopiesEditText.getError() == null && TextUtils.isEmpty(mCopiesEditText.getText())) { mIgnoreNextCopiesChange = true; @@ -1674,13 +1728,62 @@ public class PrintJobConfigActivity extends Activity { mCopiesEditText.selectAll(); mCopiesEditText.requestFocus(); } - mCopiesEditText.setEnabled(true); + + return someAttributeSelectionChanged; + } + } + + private boolean setMediaSizeSpinnerSelectionNoCallback(int position) { + if (mMediaSizeSpinner.getSelectedItemPosition() != position) { + mIgnoreNextMediaSizeChange = true; + mMediaSizeSpinner.setSelection(position); + return true; + } + return false; + } + + private void updateUiForNewPrinterCapabilities() { + // 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(); + } + + private void startSelectPrinterActivity() { + mIgnoreNextDestinationChange = true; + mDestinationSpinner.setSelection(0); + Intent intent = new Intent(PrintJobConfigActivity.this, + SelectPrinterActivity.class); + startActivityForResult(intent, ACTIVITY_REQUEST_SELECT_PRINTER); } private boolean hasErrors() { - return mRangeEditText.getError() != null - || mCopiesEditText.getError() != null; + if (mCopiesEditText.getError() != null) { + return true; + } + return mPageRangeEditText.getVisibility() == View.VISIBLE + && mPageRangeEditText.getError() != null; } // private boolean hasPdfViewer() { @@ -1708,29 +1811,49 @@ public class PrintJobConfigActivity extends Activity { implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{ private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>(); - public final PrinterInfo mFakePdfPrinter; + private final PrinterInfo mFakePdfPrinter; + + private PrinterId mLastShownPrinterId; public DestinationAdapter() { getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this); mFakePdfPrinter = createFakePdfPrinter(); } + public int getPrinterIndex(PrinterId printerId) { + for (int i = 0; i < getCount(); i++) { + PrinterInfo printer = (PrinterInfo) getItem(i); + if (printer != null && printer.getId().equals(printerId)) { + return i; + } + } + return AdapterView.INVALID_POSITION; + } + + public void ensurePrinterShownPrinterShown(PrinterId printerId) { + mLastShownPrinterId = printerId; + ensureLastShownPrinterInPosition(); + } + @Override public int getCount() { - return Math.max(Math.min(mPrinters.size(), DEST_ADAPTER_MAX_ITEM_COUNT), - DEST_ADAPTER_MIN_ITEM_COUNT); + return Math.min(mPrinters.size() + 2, DEST_ADAPTER_MAX_ITEM_COUNT); } @Override public Object getItem(int position) { - if (position == DEST_ADAPTER_POSITION_SAVE_AS_PDF) { - return mFakePdfPrinter; - } - if (!mPrinters.isEmpty()) { - if (position < DEST_ADAPTER_POSITION_SAVE_AS_PDF) { + if (mPrinters.isEmpty()) { + if (position == 0) { + return mFakePdfPrinter; + } + } else { + if (position < 1) { return mPrinters.get(position); - } else if (position > DEST_ADAPTER_POSITION_SAVE_AS_PDF - && position < getCount() - 1) { + } + if (position == 1) { + return mFakePdfPrinter; + } + if (position < getCount() - 1) { return mPrinters.get(position - 1); } } @@ -1739,15 +1862,17 @@ public class PrintJobConfigActivity extends Activity { @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; + if (position == 0) { + return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF; + } + } else { + if (position == 1) { + return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF; + } + if (position == getCount() - 1) { + return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS; } - } else if (position == getCount() - 1) { - return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS; } return position; } @@ -1768,11 +1893,15 @@ public class PrintJobConfigActivity extends Activity { CharSequence title = null; CharSequence subtitle = null; - if (mPrinters.isEmpty() - && position == DEST_ADAPTER_POSITION_SEARCHING_FOR_PRINTERS) { - title = getString(R.string.searching_for_printers); + if (mPrinters.isEmpty()) { + if (position == 0) { + PrinterInfo printer = (PrinterInfo) getItem(position); + title = printer.getName(); + } else if (position == 1) { + title = getString(R.string.all_printers); + } } else { - if (position == DEST_ADAPTER_POSITION_SAVE_AS_PDF) { + if (position == 1) { PrinterInfo printer = (PrinterInfo) getItem(position); title = printer.getName(); } else if (position == getCount() - 1) { @@ -1818,6 +1947,7 @@ public class PrintJobConfigActivity extends Activity { List<PrinterInfo> printers) { mPrinters.clear(); mPrinters.addAll(printers); + ensureLastShownPrinterInPosition(); notifyDataSetChanged(); } @@ -1827,6 +1957,27 @@ public class PrintJobConfigActivity extends Activity { notifyDataSetInvalidated(); } + private void ensureLastShownPrinterInPosition() { + if (mLastShownPrinterId == null) { + return; + } + final int printerCount = mPrinters.size(); + for (int i = 0; i < printerCount; i++) { + PrinterInfo printer = (PrinterInfo) mPrinters.get(i); + if (printer.getId().equals(mLastShownPrinterId)) { + // If already in the list - do nothing. + if (i < getCount() - 1) { + return; + } + // Else replace the last one. + final int lastPrinter = getCount() - 2; + mPrinters.set(i, mPrinters.get(lastPrinter - 1)); + mPrinters.set(lastPrinter - 1, printer); + return; + } + } + } + private PrinterInfo createFakePdfPrinter() { PrinterId printerId = new PrinterId(getComponentName(), "PDF printer"); diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index fda64c9..c1f4180 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -103,8 +103,6 @@ public final class PrintSpoolerService extends Service { private NotificationController mNotificationController; - private PrinterDiscoverySession mDiscoverySession; - public static PrintSpoolerService peekInstance() { synchronized (sLock) { return sInstance; @@ -225,61 +223,9 @@ public final class PrintSpoolerService extends Service { HandlerCallerCallback.MSG_SET_CLIENT, client); mHandlerCaller.executeOrSendMessage(message); } - - @Override - public void onPrintersAdded(List<PrinterInfo> printers) { - Message message = mHandlerCaller.obtainMessageO( - HandlerCallerCallback.MSG_ON_PRINTERS_ADDED, printers); - mHandlerCaller.executeOrSendMessage(message); - } - - @Override - public void onPrintersRemoved(List<PrinterId> printerIds) { - Message message = mHandlerCaller.obtainMessageO( - HandlerCallerCallback.MSG_ON_PRINTERS_REMOVED, printerIds); - mHandlerCaller.executeOrSendMessage(message); - } - - @Override - public void onPrintersUpdated(List<PrinterInfo> printers) { - Message message = mHandlerCaller.obtainMessageO( - HandlerCallerCallback.MSG_ON_PRINTERS_UPDATED, printers); - mHandlerCaller.executeOrSendMessage(message); - } }; } - public void createPrinterDiscoverySession() { - Message message = mHandlerCaller.obtainMessage( - HandlerCallerCallback.MSG_CREATE_PRINTER_DISCOVERY_SESSION); - mHandlerCaller.executeOrSendMessage(message); - } - - public void destroyPrinterDiscoverySession() { - Message message = mHandlerCaller.obtainMessage( - HandlerCallerCallback.MSG_DESTROY_PRINTER_DISCOVERY_SESSION); - mHandlerCaller.executeOrSendMessage(message); - } - - public void startPrinterDiscovery(List<PrinterId> priorityList) { - Message message = mHandlerCaller.obtainMessageO( - HandlerCallerCallback.MSG_START_PRINTER_DISCOVERY, priorityList); - mHandlerCaller.executeOrSendMessage(message); - } - - public void stopPrinterDiscovery() { - Message message = mHandlerCaller.obtainMessage( - HandlerCallerCallback.MSG_STOP_PRINTER_DISCOVERY); - mHandlerCaller.executeOrSendMessage(message); - } - - public void requestPrinterUpdate(PrinterId pritnerId) { - Message message = mHandlerCaller.obtainMessageO( - HandlerCallerCallback.MSG_REQUEST_PRINTER_UPDATE, pritnerId); - mHandlerCaller.executeOrSendMessage(message); - } - - private void sendOnPrintJobQueued(PrintJobInfo printJob) { Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob); @@ -299,16 +245,6 @@ public final class PrintSpoolerService extends Service { } 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; @@ -317,81 +253,8 @@ public final class PrintSpoolerService extends Service { public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 14; @Override - @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<PrinterId> priorityList = (ArrayList<PrinterId>) 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: { synchronized (mLock) { mClient = (IPrintSpoolerClient) message.obj; @@ -452,39 +315,6 @@ public final class PrintSpoolerService extends Service { case MSG_CHECK_ALL_PRINTJOBS_HANDLED: { checkAllPrintJobsHandled(); } break; - - case MSG_ON_PRINTERS_ADDED: { - final PrinterDiscoverySession session; - synchronized (mLock) { - session = mDiscoverySession; - } - if (session != null) { - List<PrinterInfo> printers = (ArrayList<PrinterInfo>) message.obj; - session.onPrintersAdded(printers); - } - } break; - - case MSG_ON_PRINTERS_REMOVED: { - final PrinterDiscoverySession session; - synchronized (mLock) { - session = mDiscoverySession; - } - if (session != null) { - List<PrinterId> printerIds = (ArrayList<PrinterId>) message.obj; - session.onPrintersRemoved(printerIds); - } - } break; - - case MSG_ON_PRINTERS_UPDATED: { - final PrinterDiscoverySession session; - synchronized (mLock) { - session = mDiscoverySession; - } - if (session != null) { - List<PrinterInfo> printers = (ArrayList<PrinterInfo>) message.obj; - session.onPrintersUpdated(printers); - } - } break; } } } @@ -583,12 +413,6 @@ public final class PrintSpoolerService extends Service { } } - private void setPrinterDiscoverySessionClient(PrinterDiscoverySession session) { - synchronized (mLock) { - mDiscoverySession = session; - } - } - private int generatePrintJobIdLocked() { int printJobId = sPrintJobIdCounter++; while (isDuplicatePrintJobId(printJobId)) { @@ -1352,46 +1176,4 @@ public final class PrintSpoolerService extends Service { 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<PrinterId> 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<PrinterInfo> printers); - - public abstract void onPrintersRemoved(List<PrinterId> printerIds); - - public abstract void onPrintersUpdated(List<PrinterInfo> printers); - } } diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java index 8ee2fea..671a5dc 100644 --- a/services/java/com/android/server/print/PrintManagerService.java +++ b/services/java/com/android/server/print/PrintManagerService.java @@ -31,8 +31,10 @@ import android.os.UserHandle; import android.print.IPrintClient; import android.print.IPrintDocumentAdapter; import android.print.IPrintManager; +import android.print.IPrinterDiscoveryObserver; import android.print.PrintAttributes; import android.print.PrintJobInfo; +import android.print.PrinterId; import android.provider.Settings; import android.util.SparseArray; @@ -188,6 +190,84 @@ public final class PrintManagerService extends IPrintManager.Stub { } } + @Override + public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer, + int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.createPrinterDiscoverySession(observer); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer, + int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.destroyPrinterDiscoverySession(observer); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void startPrinterDiscovery(IPrinterDiscoveryObserver observer, + List<PrinterId> priorityList, int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.startPrinterDiscovery(observer, priorityList); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.stopPrinterDiscovery(observer); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void requestPrinterUpdate(PrinterId printerId, int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.requestPrinterUpdate(printerId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private void registerContentObservers() { final Uri enabledPrintServicesUri = Settings.Secure.getUriFor( Settings.Secure.ENABLED_PRINT_SERVICES); diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java index 5c68460..7f4b343 100644 --- a/services/java/com/android/server/print/RemotePrintService.java +++ b/services/java/com/android/server/print/RemotePrintService.java @@ -61,6 +61,8 @@ final class RemotePrintService implements DeathRecipient { private final RemotePrintSpooler mSpooler; + private final UserState mUserState; + private final int mUserId; private final List<Runnable> mPendingCommands = new ArrayList<Runnable>(); @@ -82,8 +84,9 @@ final class RemotePrintService implements DeathRecipient { private boolean mHasPrinterDiscoverySession; public RemotePrintService(Context context, ComponentName componentName, int userId, - RemotePrintSpooler spooler) { + RemotePrintSpooler spooler, UserState userState) { mContext = context; + mUserState = userState; mComponentName = componentName; mIntent = new Intent().setComponent(mComponentName); mUserId = userId; @@ -561,7 +564,7 @@ final class RemotePrintService implements DeathRecipient { throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers); final long identity = Binder.clearCallingIdentity(); try { - service.mSpooler.onPrintersAdded(printers); + service.mUserState.onPrintersAdded(printers); } finally { Binder.restoreCallingIdentity(identity); } @@ -575,7 +578,7 @@ final class RemotePrintService implements DeathRecipient { throwIfPrinterIdsTampered(service.mComponentName, printerIds); final long identity = Binder.clearCallingIdentity(); try { - service.mSpooler.onPrintersRemoved(printerIds); + service.mUserState.onPrintersRemoved(printerIds); } finally { Binder.restoreCallingIdentity(identity); } @@ -589,7 +592,7 @@ final class RemotePrintService implements DeathRecipient { throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, printers); final long identity = Binder.clearCallingIdentity(); try { - service.mSpooler.onPrintersUpdated(printers); + service.mUserState.onPrintersUpdated(printers); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java index d261288..db0eb33 100644 --- a/services/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/java/com/android/server/print/RemotePrintSpooler.java @@ -34,8 +34,6 @@ import android.print.IPrintSpoolerCallbacks; import android.print.IPrintSpoolerClient; import android.print.PrintAttributes; import android.print.PrintJobInfo; -import android.print.PrinterId; -import android.print.PrinterInfo; import android.util.Slog; import android.util.TimedRemoteCaller; @@ -93,11 +91,6 @@ final class RemotePrintSpooler { public static interface PrintSpoolerCallbacks { public void onPrintJobQueued(PrintJobInfo printJob); public void onAllPrintJobsForServiceHandled(ComponentName printService); - public void createPrinterDiscoverySession(); - public void destroyPrinterDiscoverySession(); - public void startPrinterDiscovery(List<PrinterId> priorityList); - public void stopPrinterDiscovery(); - public void requestPrinterUpdate(PrinterId printerId); } public RemotePrintSpooler(Context context, int userId, @@ -305,78 +298,6 @@ final class RemotePrintSpooler { } } - public final void onPrintersAdded(List<PrinterInfo> printers) { - throwIfCalledOnMainThread(); - synchronized (mLock) { - throwIfDestroyedLocked(); - mCanUnbind = false; - } - try { - getRemoteInstanceLazy().onPrintersAdded(printers); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error adding printers.", re); - } catch (TimeoutException te) { - Slog.e(LOG_TAG, "Error adding printers.", te); - } finally { - if (DEBUG) { - Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() - + "] onPrintersAdded()"); - } - synchronized (mLock) { - mCanUnbind = true; - mLock.notifyAll(); - } - } - } - - public final void onPrintersRemoved(List<PrinterId> printerIds) { - throwIfCalledOnMainThread(); - synchronized (mLock) { - throwIfDestroyedLocked(); - mCanUnbind = false; - } - try { - getRemoteInstanceLazy().onPrintersRemoved(printerIds); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error removing printers.", re); - } catch (TimeoutException te) { - Slog.e(LOG_TAG, "Error removing printers.", te); - } finally { - if (DEBUG) { - Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() - + "] onPrintersRemoved()"); - } - synchronized (mLock) { - mCanUnbind = true; - mLock.notifyAll(); - } - } - } - - public final void onPrintersUpdated(List<PrinterInfo> printers) { - throwIfCalledOnMainThread(); - synchronized (mLock) { - throwIfDestroyedLocked(); - mCanUnbind = false; - } - try { - getRemoteInstanceLazy().onPrintersUpdated(printers); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error updating printers.", re); - } catch (TimeoutException te) { - Slog.e(LOG_TAG, "Error updating printers.", te); - } finally { - if (DEBUG) { - Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() - + "] onPrintersUpdted()"); - } - synchronized (mLock) { - mCanUnbind = true; - mLock.notifyAll(); - } - } - } - private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException { synchronized (mLock) { if (mRemoteInstance != null) { @@ -672,70 +593,5 @@ final class RemotePrintSpooler { } } } - - @Override - public void createPrinterDiscoverySession() { - RemotePrintSpooler spooler = mWeakSpooler.get(); - if (spooler != null) { - final long identity = Binder.clearCallingIdentity(); - try { - spooler.mCallbacks.createPrinterDiscoverySession(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void destroyPrinterDiscoverySession() { - RemotePrintSpooler spooler = mWeakSpooler.get(); - if (spooler != null) { - final long identity = Binder.clearCallingIdentity(); - try { - spooler.mCallbacks.destroyPrinterDiscoverySession(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void startPrinterDiscovery(List<PrinterId> priorityList) { - RemotePrintSpooler spooler = mWeakSpooler.get(); - if (spooler != null) { - final long identity = Binder.clearCallingIdentity(); - try { - spooler.mCallbacks.startPrinterDiscovery(priorityList); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void stopPrinterDiscovery() { - RemotePrintSpooler spooler = mWeakSpooler.get(); - if (spooler != null) { - final long identity = Binder.clearCallingIdentity(); - try { - spooler.mCallbacks.stopPrinterDiscovery(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void requestPrinterUpdate(PrinterId printerId) { - RemotePrintSpooler spooler = mWeakSpooler.get(); - if (spooler != null) { - final long identity = Binder.clearCallingIdentity(); - try { - spooler.mCallbacks.requestPrinterUpdate(printerId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } } } diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index 9d7cfdd..c979a11 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -21,14 +21,26 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.print.IPrinterDiscoveryObserver; import android.print.PrintJobInfo; import android.print.PrinterId; +import android.print.PrinterInfo; import android.printservice.PrintServiceInfo; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; +import android.util.ArrayMap; +import android.util.Log; import android.util.Slog; +import com.android.internal.os.SomeArgs; import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; import java.util.ArrayList; @@ -45,6 +57,10 @@ final class UserState implements PrintSpoolerCallbacks { private static final String LOG_TAG = "UserState"; + private static final boolean DEBUG = true && Build.IS_DEBUGGABLE; + + private static final int MAX_ITEMS_PER_CALLBACK = 100; + private static final char COMPONENT_NAME_SEPARATOR = ':'; private final SimpleStringSplitter mStringColonSplitter = @@ -70,6 +86,8 @@ final class UserState implements PrintSpoolerCallbacks { private final RemotePrintSpooler mSpooler; + private PrinterDiscoverySessionMediator mPrinterDiscoverySession; + private boolean mDestroyed; public UserState(Context context, int userId, Object lock) { @@ -104,86 +122,135 @@ final class UserState implements PrintSpoolerCallbacks { } } - @Override - public void createPrinterDiscoverySession() { - final List<RemotePrintService> services; + public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); if (mActiveServices.isEmpty()) { return; } - services = new ArrayList<RemotePrintService>(mActiveServices.values()); + if (mPrinterDiscoverySession == null) { + // If we do not have a session, tell all service to create one. + mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) { + @Override + public void onDestroyed() { + mPrinterDiscoverySession = null; + } + }; + // Add the observer to the brand new session. + mPrinterDiscoverySession.addObserverLocked(observer); + } else { + // If services have created session, just add the observer. + mPrinterDiscoverySession.addObserverLocked(observer); + } } - final int serviceCount = services.size(); - for (int i = 0; i < serviceCount; i++) { - RemotePrintService service = services.get(i); - service.createPrinterDiscoverySession(); + } + + public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { + synchronized (mLock) { + // Already destroyed - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Remove this observer. + mPrinterDiscoverySession.removeObserverLocked(observer); } } - @Override - public void destroyPrinterDiscoverySession() { - final List<RemotePrintService> services; + public void startPrinterDiscovery(IPrinterDiscoveryObserver observer, + List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); + // No services - nothing to do. if (mActiveServices.isEmpty()) { return; } - services = new ArrayList<RemotePrintService>(mActiveServices.values()); - } - final int serviceCount = services.size(); - for (int i = 0; i < serviceCount; i++) { - RemotePrintService service = services.get(i); - service.destroyPrinterDiscoverySession(); + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Kick of discovery. + mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer, + printerIds); } } - @Override - public void startPrinterDiscovery(List<PrinterId> printerIds) { - final List<RemotePrintService> services; + public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); + // No services - nothing to do. if (mActiveServices.isEmpty()) { return; } - services = new ArrayList<RemotePrintService>(mActiveServices.values()); - } - final int serviceCount = services.size(); - for (int i = 0; i < serviceCount; i++) { - RemotePrintService service = services.get(i); - service.startPrinterDiscovery(printerIds); + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Kick of discovery. + mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer); } } - @Override - public void stopPrinterDiscovery() { - final List<RemotePrintService> services; + public void requestPrinterUpdate(PrinterId printerId) { synchronized (mLock) { throwIfDestroyedLocked(); + // No services - nothing to do. if (mActiveServices.isEmpty()) { return; } - services = new ArrayList<RemotePrintService>(mActiveServices.values()); + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Request an updated. + mPrinterDiscoverySession.requestPrinterUpdateLocked(printerId); } - final int serviceCount = services.size(); - for (int i = 0; i < serviceCount; i++) { - RemotePrintService service = services.get(i); - service.stopPrinterDiscovery(); + } + + public void onPrintersAdded(List<PrinterInfo> printers) { + synchronized (mLock) { + throwIfDestroyedLocked(); + // No services - nothing to do. + if (mActiveServices.isEmpty()) { + return; + } + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Request an updated. + mPrinterDiscoverySession.onPrintersAddedLocked(printers); } } - @Override - public void requestPrinterUpdate(PrinterId printerId) { - final RemotePrintService service; + public void onPrintersRemoved(List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); + // No services - nothing to do. if (mActiveServices.isEmpty()) { return; } - service = mActiveServices.get(printerId.getServiceName()); + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Request an updated. + mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds); } - if (service != null) { - service.requestPrinterUpdate(printerId); + } + + public void onPrintersUpdated(List<PrinterInfo> printers) { + synchronized (mLock) { + throwIfDestroyedLocked(); + // No services - nothing to do. + if (mActiveServices.isEmpty()) { + return; + } + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Request an updated. + mPrinterDiscoverySession.onPrintersUpdatedLocked(printers); } } @@ -222,6 +289,10 @@ final class UserState implements PrintSpoolerCallbacks { mActiveServices.clear(); mInstalledServices.clear(); mEnabledServices.clear(); + if (mPrinterDiscoverySession != null) { + mPrinterDiscoverySession.destroyLocked(); + mPrinterDiscoverySession = null; + } mDestroyed = true; } @@ -302,13 +373,20 @@ final class UserState implements PrintSpoolerCallbacks { resolveInfo.serviceInfo.name); if (mEnabledServices.contains(serviceName)) { if (!mActiveServices.containsKey(serviceName)) { - mActiveServices.put(serviceName, new RemotePrintService( - mContext, serviceName, mUserId, mSpooler)); + RemotePrintService service = new RemotePrintService( + mContext, serviceName, mUserId, mSpooler, this); + mActiveServices.put(serviceName, service); + if (mPrinterDiscoverySession != null) { + mPrinterDiscoverySession.onServiceAddedLocked(service); + } } } else { RemotePrintService service = mActiveServices.remove(serviceName); if (service != null) { service.destroy(); + if (mPrinterDiscoverySession != null) { + mPrinterDiscoverySession.onServiceRemovedLocked(serviceName); + } } } } @@ -319,5 +397,496 @@ final class UserState implements PrintSpoolerCallbacks { throw new IllegalStateException("Cannot interact with a destroyed instance."); } } -} + private class PrinterDiscoverySessionMediator { + private final ArrayMap<PrinterId, PrinterInfo> mPrinters = + new ArrayMap<PrinterId, PrinterInfo>(); + + private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers = + new RemoteCallbackList<IPrinterDiscoveryObserver>() { + @Override + public void onCallbackDied(IPrinterDiscoveryObserver observer) { + synchronized (mLock) { + stopPrinterDiscoveryLocked(observer); + removeObserverLocked(observer); + } + } + }; + + private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>(); + + private final Handler mHandler; + + private boolean mIsDestroyed; + + public PrinterDiscoverySessionMediator(Context context) { + mHandler = new SessionHandler(context.getMainLooper()); + // Kick off the session creation. + List<RemotePrintService> services = new ArrayList<RemotePrintService>( + mActiveServices.values()); + mHandler.obtainMessage(SessionHandler + .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services) + .sendToTarget(); + } + + public void addObserverLocked(IPrinterDiscoveryObserver observer) { + // Add the observer. + mDiscoveryObservers.register(observer); + + // Bring the added observer up to speed with the printers. + if (!mPrinters.isEmpty()) { + List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values()); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = observer; + args.arg2 = printers; + mHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED, + args).sendToTarget(); + } + } + + public void removeObserverLocked(IPrinterDiscoveryObserver observer) { + // Remove the observer. + mDiscoveryObservers.unregister(observer); + // No one else observing - then kill it. + if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) { + destroyLocked(); + } + } + + public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer, + List<PrinterId> priorityList) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not starting dicovery - session destroyed"); + return; + } + + // If printer discovery is ongoing and the start request has a list + // of printer to be checked, then we just request refreshing each of + // them rather making another start discovery request. + if (!mStartedPrinterDiscoveryTokens.isEmpty() + && priorityList != null && !priorityList.isEmpty()) { + final int priorityIdCount = priorityList.size(); + for (int i = 0; i < priorityIdCount; i++) { + requestPrinterUpdate(priorityList.get(i)); + } + return; + } + + // Remember we got a start request to match with an end. + mStartedPrinterDiscoveryTokens.add(observer.asBinder()); + // The service are already performing discovery - nothing to do. + if (mStartedPrinterDiscoveryTokens.size() > 1) { + return; + } + List<RemotePrintService> services = new ArrayList<RemotePrintService>( + mActiveServices.values()); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = services; + args.arg2 = priorityList; + mHandler.obtainMessage(SessionHandler + .MSG_DISPATCH_START_PRINTER_DISCOVERY, args) + .sendToTarget(); + } + + public final void stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not stopping dicovery - session destroyed"); + return; + } + // This one did not make an active discovery request - nothing to do. + if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) { + return; + } + // There are other interested observers - do not stop discovery. + if (!mStartedPrinterDiscoveryTokens.isEmpty()) { + return; + } + List<RemotePrintService> services = new ArrayList<RemotePrintService>( + mActiveServices.values()); + mHandler.obtainMessage(SessionHandler + .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services) + .sendToTarget(); + } + + public void requestPrinterUpdateLocked(PrinterId printerId) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not updating pritner - session destroyed"); + return; + } + RemotePrintService service = mActiveServices.get(printerId.getServiceName()); + if (service != null) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = service; + args.arg2 = printerId; + mHandler.obtainMessage(SessionHandler + .MSG_REQUEST_PRINTER_UPDATE, args) + .sendToTarget(); + } + } + + public void onDestroyed() { + /* do nothing */ + } + + public void destroyLocked() { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not destroying - session destroyed"); + return; + } + // Make sure discovery is stopped. + final int observerCount = mStartedPrinterDiscoveryTokens.size(); + for (int i = 0; i < observerCount; i++) { + IBinder token = mStartedPrinterDiscoveryTokens.get(i); + stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token)); + } + // Tell the services we are done. + List<RemotePrintService> services = new ArrayList<RemotePrintService>( + mActiveServices.values()); + mHandler.obtainMessage(SessionHandler + .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services) + .sendToTarget(); + } + + public void onPrintersAddedLocked(List<PrinterInfo> printers) { + if (DEBUG) { + Log.i(LOG_TAG, "onPrintersAddedLocked()"); + } + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not adding printers - session destroyed"); + return; + } + List<PrinterInfo> addedPrinters = null; + 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); + if (addedPrinters == null) { + addedPrinters = new ArrayList<PrinterInfo>(); + } + addedPrinters.add(printer); + } + } + if (addedPrinters != null) { + mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, + addedPrinters).sendToTarget(); + } + } + + public void onPrintersRemovedLocked(List<PrinterId> printerIds) { + if (DEBUG) { + Log.i(LOG_TAG, "onPrintersRemovedLocked()"); + } + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not removing printers - session destroyed"); + return; + } + List<PrinterId> removedPrinterIds = null; + final int removedPrinterCount = printerIds.size(); + for (int i = 0; i < removedPrinterCount; i++) { + PrinterId removedPrinterId = printerIds.get(i); + if (mPrinters.remove(removedPrinterId) != null) { + if (removedPrinterIds == null) { + removedPrinterIds = new ArrayList<PrinterId>(); + } + removedPrinterIds.add(removedPrinterId); + } + } + if (removedPrinterIds != null) { + mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, + removedPrinterIds).sendToTarget(); + } + } + + public void onPrintersUpdatedLocked(List<PrinterInfo> printers) { + if (DEBUG) { + Log.i(LOG_TAG, "onPrintersUpdatedLocked()"); + } + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not updating printers - session destroyed"); + return; + } + List<PrinterInfo> updatedPrinters = null; + 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); + if (updatedPrinters == null) { + updatedPrinters = new ArrayList<PrinterInfo>(); + } + updatedPrinters.add(updatedPrinter); + } + } + if (updatedPrinters != null) { + mHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_UPDATED, + updatedPrinters).sendToTarget(); + } + } + + public void onServiceRemovedLocked(ComponentName serviceName) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not updating removed service - session destroyed"); + return; + } + // No printers - nothing to do. + if (mPrinters.isEmpty()) { + return; + } + // Remove the printers for that service. + List<PrinterInfo> removedPrinters = null; + final int printerCount = mPrinters.size(); + for (int i = 0; i < printerCount; i++) { + PrinterInfo printer = mPrinters.get(i); + if (printer.getId().getServiceName().equals(serviceName)) { + if (removedPrinters == null) { + removedPrinters = new ArrayList<PrinterInfo>(); + } + removedPrinters.add(printer); + } + } + if (!removedPrinters.isEmpty()) { + mHandler.obtainMessage( + SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, + removedPrinters).sendToTarget(); + } + } + + public void onServiceAddedLocked(RemotePrintService service) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not updating added service - session destroyed"); + return; + } + // Tell the service to create a session. + mHandler.obtainMessage( + SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION, + service).sendToTarget(); + // If there are some observers that started discovery - tell the service. + if (mDiscoveryObservers.getRegisteredCallbackCount() > 0) { + mHandler.obtainMessage( + SessionHandler.MSG_START_PRINTER_DISCOVERY, + service).sendToTarget(); + } + } + + private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) { + final int observerCount = mDiscoveryObservers.beginBroadcast(); + for (int i = 0; i < observerCount; i++) { + IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); + try { + observer.onPrintersAdded(addedPrinters); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error dispatching added printers", re); + } + } + mDiscoveryObservers.finishBroadcast(); + } + + private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) { + final int observerCount = mDiscoveryObservers.beginBroadcast(); + for (int i = 0; i < observerCount; i++) { + IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); + try { + observer.onPrintersRemoved(removedPrinterIds); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error dispatching removed printers", re); + } + } + mDiscoveryObservers.finishBroadcast(); + } + + private void handleDispatchPrintersUpdated(List<PrinterInfo> updatedPrinters) { + final int observerCount = mDiscoveryObservers.beginBroadcast(); + for (int i = 0; i < observerCount; i++) { + IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); + try { + observer.onPrintersUpdated(updatedPrinters); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error dispatching updated printers", re); + } + } + mDiscoveryObservers.finishBroadcast(); + } + + private void handleDispatchCreatePrinterDiscoverySession( + List<RemotePrintService> services) { + final int serviceCount = services.size(); + for (int i = 0; i < serviceCount; i++) { + RemotePrintService service = services.get(i); + service.createPrinterDiscoverySession(); + } + } + + private void handleDispatchDestroyPrinterDiscoverySession( + List<RemotePrintService> services) { + final int serviceCount = services.size(); + for (int i = 0; i < serviceCount; i++) { + RemotePrintService service = services.get(i); + service.destroyPrinterDiscoverySession(); + } + onDestroyed(); + } + + private void handleDispatchStartPrinterDiscovery( + List<RemotePrintService> services, List<PrinterId> printerIds) { + final int serviceCount = services.size(); + for (int i = 0; i < serviceCount; i++) { + RemotePrintService service = services.get(i); + service.startPrinterDiscovery(printerIds); + } + } + + private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) { + final int serviceCount = services.size(); + for (int i = 0; i < serviceCount; i++) { + RemotePrintService service = services.get(i); + service.stopPrinterDiscovery(); + } + } + + private void handleRequestPrinterUpdate(RemotePrintService service, + PrinterId printerId) { + service.requestPrinterUpdate(printerId); + } + + private void handlePrintersAdded(IPrinterDiscoveryObserver observer, + List<PrinterInfo> printers) { + try { + final int printerCount = printers.size(); + if (printerCount <= MAX_ITEMS_PER_CALLBACK) { + observer.onPrintersAdded(printers); + } else { + // Send the added printers in chunks avoiding the binder transaction limit. + final int transactionCount = (printerCount / MAX_ITEMS_PER_CALLBACK) + 1; + for (int i = 0; i < transactionCount; i++) { + final int start = i * MAX_ITEMS_PER_CALLBACK; + final int end = Math.min(start + MAX_ITEMS_PER_CALLBACK, printerCount); + List<PrinterInfo> subPrinters = printers.subList(start, end); + observer.onPrintersAdded(subPrinters); + } + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error sending added printers", re); + } + } + + private void handlePrintersRemoved(IPrinterDiscoveryObserver observer, + List<PrinterId> printerIds) { + try { + final int printerCount = printerIds.size(); + if (printerCount <= MAX_ITEMS_PER_CALLBACK) { + observer.onPrintersRemoved(printerIds); + } 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<PrinterId> subPrinterIds = printerIds.subList(start, end); + observer.onPrintersRemoved(subPrinterIds); + } + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error sending added printers", re); + } + } + + private final class SessionHandler extends Handler { + public static final int MSG_PRINTERS_ADDED = 1; + public static final int MSG_PRINTERS_REMOVED = 2; + public static final int MSG_DISPATCH_PRINTERS_ADDED = 3; + public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4; + public static final int MSG_DISPATCH_PRINTERS_UPDATED = 5; + + public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 6; + public static final int MSG_START_PRINTER_DISCOVERY = 7; + public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 8; + public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 9; + public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 10; + public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 11; + public static final int MSG_REQUEST_PRINTER_UPDATE = 12; + + SessionHandler(Looper looper) { + super(looper, null, false); + } + + @Override + @SuppressWarnings("unchecked") + public void handleMessage(Message message) { + switch (message.what) { + case MSG_PRINTERS_ADDED: { + SomeArgs args = (SomeArgs) message.obj; + IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; + List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2; + args.recycle(); + handlePrintersAdded(observer, addedPrinters); + } break; + + case MSG_PRINTERS_REMOVED: { + SomeArgs args = (SomeArgs) message.obj; + IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; + List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2; + args.recycle(); + handlePrintersRemoved(observer, removedPrinterIds); + } + + case MSG_DISPATCH_PRINTERS_ADDED: { + List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj; + handleDispatchPrintersAdded(addedPrinters); + } break; + + case MSG_DISPATCH_PRINTERS_REMOVED: { + List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj; + handleDispatchPrintersRemoved(removedPrinterIds); + } break; + + case MSG_DISPATCH_PRINTERS_UPDATED: { + List<PrinterInfo> updatedPrinters = (List<PrinterInfo>) message.obj; + handleDispatchPrintersUpdated(updatedPrinters); + } break; + + case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { + RemotePrintService service = (RemotePrintService) message.obj; + service.createPrinterDiscoverySession(); + } break; + + case MSG_START_PRINTER_DISCOVERY: { + RemotePrintService service = (RemotePrintService) message.obj; + service.startPrinterDiscovery(null); + } break; + + case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: { + List<RemotePrintService> services = (List<RemotePrintService>) message.obj; + handleDispatchCreatePrinterDiscoverySession(services); + } break; + + case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: { + List<RemotePrintService> services = (List<RemotePrintService>) message.obj; + handleDispatchDestroyPrinterDiscoverySession(services); + } break; + + case MSG_DISPATCH_START_PRINTER_DISCOVERY: { + SomeArgs args = (SomeArgs) message.obj; + List<RemotePrintService> services = (List<RemotePrintService>) args.arg1; + List<PrinterId> printerIds = (List<PrinterId>) args.arg2; + args.recycle(); + handleDispatchStartPrinterDiscovery(services, printerIds); + } break; + + case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: { + List<RemotePrintService> services = (List<RemotePrintService>) message.obj; + handleDispatchStopPrinterDiscovery(services); + } break; + + case MSG_REQUEST_PRINTER_UPDATE: { + SomeArgs args = (SomeArgs) message.obj; + RemotePrintService service = (RemotePrintService) args.arg1; + PrinterId printerId = (PrinterId) args.arg2; + args.recycle(); + handleRequestPrinterUpdate(service, printerId); + } break; + } + } + } + } +}
\ No newline at end of file |