From 66c96591e2ddb464c67e60dbf4193ef4ec8a620b Mon Sep 17 00:00:00 2001
From: "Philip P. Moltmann"
Date: Wed, 24 Feb 2016 11:32:43 -0800
Subject: [PATCH] Add "app printer activity" and always keep the print service
state updated. Also fiddle with the UI to use more standard values.
To be sure the print service state alwasy updated I changed
PrintManager.getPrintServices to return a loader which just wraps a
registerListener/getList/removeListener combo.
I also added a new function to enabled/disable a print service to be
keep all updating logic inside the PrintManagerService->UserState.
Then I changed all code to use this new interface.
Detailed comments:
PrintServiceInfo:
- I had to add the enabled state to the PrintServiceInfo as some users
of PrintManager.getPrintServices want all services but then display
different data depending on the enabled state. Of course I could have
created two PrintManager.getPrintServices-loaders to load the two
separate list of services. I think it is much easier to add this
property though. It is updated every time new data is returned to the
PrintManager.getPrintServices-loader.
AddPrinterActivity:
- This is shown as a dialog-style overlay to indicate that the user will
return to the select-printers activity. It contains of three list that
are updated via separate loaders.
- The recommended services will be added later to keep this path set
small.
PrintActivity:
- There are two small places where we have to update the data when we
get a new list of print services.
- In very, very rare conditions it can happen that the print service
of the current printer gains or looses the "advancedOptions"
activity
- If we have no enabled print services we want to show "Add printer"
instead of "All printers...".
- Also the print registry is not the only loader anymore, hence we have
to assign loader ids to it to not conflict with the other loaders in
this activity.
- Small bug in onPrintersChanged: If a printer is selected and the print
service of this printer gets disabled the holder goes into "removed"
state which disables the printer. When the print service is then
enabled again, we forgot to re-enable the holder.
PrinterRegistry:
- The registry assumed that the FusedPrinterProvider was the only loader
in the activity. This is not true anymore, hence it has to assign the
appropriate loader ids.
- The FusedPrinterProvider has an internal loader, hence we have to
forward a loader Id into it.
- The PrintRegistry is only called backed for a single loader, hence no
need to check the loader-id.
SelectPrinterActivity:
- The AddPrinterDialog was removed as we now have the
AddPrinterActivity.
- Added a loader for the enabled services to update the empty state.
- Added dedicated loader Id for the PrinterRegistry again.
- If we have no enabled services, the SelectPrinterActivity chainloads
the AddPrinterActivity as this is the only thing the user can do
anyway. "Save a click". This should only happen when the activity is
create the first time.
- Moved the "add printer" from the menu item to the list of printers as
suggested by UX and Zach.
PrintManagerService, UserState and IPrintManagerParamtersTest:
- As the only place where the print service state is updated is now the
userstate, we have no more sychronization problems. Whohoo.
- The users can now register for changes to the print services similar
as they can register for changes to the print jobs.
- UserState.getPrintServices is the only function can exposes any
knowledge of the print services to the outside world.
Change-Id: I9be2c7300431e06aaff9bdf7eb36120d869b56ac
---
Android.mk | 1 +
core/java/android/print/IPrintManager.aidl | 45 +-
.../print/IPrintServicesChangeListener.aidl | 26 +
core/java/android/print/PrintManager.java | 184 +++++-
.../android/print/PrintServicesLoader.java | 125 ++++
.../printservice/PrintServiceInfo.java | 23 +
.../src/android/print/BasePrintTest.java | 6 +-
.../print/IPrintManagerParametersTest.java | 166 ++++--
packages/PrintSpooler/AndroidManifest.xml | 11 +-
packages/PrintSpooler/res/drawable/ic_add.xml | 2 +-
.../PrintSpooler/res/drawable/ic_print.xml | 2 +-
.../res/drawable/page_selector_background.xml | 4 +-
.../res/drawable/print_button_background.xml | 2 +-
.../res/layout/add_printer_activity.xml | 29 +
.../res/layout/add_printer_list_header.xml | 28 +
.../res/layout/add_printer_list_item.xml | 46 ++
.../layout/all_print_services_list_item.xml | 38 ++
.../disabled_print_services_list_item.xml | 55 ++
.../enabled_print_services_list_item.xml | 54 ++
.../res/layout/printer_dropdown_item.xml | 15 +-
.../res/layout/printer_dropdown_prompt.xml | 7 +-
.../res/layout/printer_list_item.xml | 45 +-
.../res/layout/select_printer_activity.xml | 17 +-
.../res/menu/select_printer_activity.xml | 8 -
packages/PrintSpooler/res/values/colors.xml | 2 -
.../res/values/donottranslate.xml | 2 +
packages/PrintSpooler/res/values/strings.xml | 32 +-
packages/PrintSpooler/res/values/styles.xml | 34 ++
packages/PrintSpooler/res/values/themes.xml | 11 +-
.../printspooler/ui/AddPrinterActivity.java | 563 ++++++++++++++++++
.../ui/FusedPrintersProvider.java | 86 ++-
.../printspooler/ui/PrintActivity.java | 114 +++-
.../printspooler/ui/PrinterRegistry.java | 68 +--
.../ui/SelectPrinterActivity.java | 360 +++++------
.../printspooler/util/PrintOptionUtils.java | 56 --
.../pm/DefaultPermissionGrantPolicy.java | 3 +-
.../server/print/PrintManagerService.java | 88 ++-
.../server/print/RemotePrintSpooler.java | 5 +-
.../com/android/server/print/UserState.java | 152 ++++-
39 files changed, 2005 insertions(+), 510 deletions(-)
create mode 100644 core/java/android/print/IPrintServicesChangeListener.aidl
create mode 100644 core/java/android/print/PrintServicesLoader.java
create mode 100644 packages/PrintSpooler/res/layout/add_printer_activity.xml
create mode 100644 packages/PrintSpooler/res/layout/add_printer_list_header.xml
create mode 100644 packages/PrintSpooler/res/layout/add_printer_list_item.xml
create mode 100644 packages/PrintSpooler/res/layout/all_print_services_list_item.xml
create mode 100644 packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
create mode 100644 packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
create mode 100644 packages/PrintSpooler/res/values/styles.xml
create mode 100644 packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
delete mode 100644 packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
diff --git a/Android.mk b/Android.mk
index 3ac5889f61c33..e39ff3bbb3c0a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -246,6 +246,7 @@ LOCAL_SRC_FILES += \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrintDocumentAdapterObserver.aidl \
core/java/android/print/IPrintJobStateChangeListener.aidl \
+ core/java/android/print/IPrintServicesChangeListener.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \
core/java/android/print/IPrintSpoolerCallbacks.aidl \
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 9a80e375ceb22..5eb8cc2f37a40 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -16,12 +16,14 @@
package android.print;
+import android.content.ComponentName;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrintDocumentAdapter;
import android.print.PrintJobId;
import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintServicesChangeListener;
import android.print.PrinterId;
import android.print.PrintJobInfo;
import android.print.PrintAttributes;
@@ -45,8 +47,47 @@ interface IPrintManager {
void removePrintJobStateChangeListener(in IPrintJobStateChangeListener listener,
int userId);
- List getInstalledPrintServices(int userId);
- List getEnabledPrintServices(int userId);
+ /**
+ * Listen for changes to the installed and enabled print services.
+ *
+ * @param listener the listener to add
+ * @param userId the id of the user listening
+ *
+ * @see android.print.PrintManager#getPrintServices(int, String)
+ */
+ void addPrintServicesChangeListener(in IPrintServicesChangeListener listener,
+ int userId);
+
+ /**
+ * Stop listening for changes to the installed and enabled print services.
+ *
+ * @param listener the listener to remove
+ * @param userId the id of the user requesting the removal
+ *
+ * @see android.print.PrintManager#getPrintServices(int, String)
+ */
+ void removePrintServicesChangeListener(in IPrintServicesChangeListener listener,
+ int userId);
+
+ /**
+ * Get the print services.
+ *
+ * @param selectionFlags flags selecting which services to get
+ * @param selectedService if not null, the id of the print service to get
+ * @param userId the id of the user requesting the services
+ *
+ * @return the list of selected print services.
+ */
+ List getPrintServices(int selectionFlags, int userId);
+
+ /**
+ * Enable or disable a print service.
+ *
+ * @param service The service to enabled or disable
+ * @param isEnabled whether the service should be enabled or disabled
+ * @param userId the id of the user requesting the services
+ */
+ void setPrintServiceEnabled(in ComponentName service, boolean isEnabled, int userId);
void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId);
void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
diff --git a/core/java/android/print/IPrintServicesChangeListener.aidl b/core/java/android/print/IPrintServicesChangeListener.aidl
new file mode 100644
index 0000000000000..2a7ce89af98ec
--- /dev/null
+++ b/core/java/android/print/IPrintServicesChangeListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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;
+
+/**
+ * Interface for observing print services changes.
+ *
+ * @hide
+ */
+oneway interface IPrintServicesChangeListener {
+ void onPrintServicesChanged();
+}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 0540036f1a496..25fc968fec5a7 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
@@ -111,6 +112,38 @@ public final class PrintManager {
private static final boolean DEBUG = false;
private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
+ private static final int MSG_NOTIFY_PRINT_SERVICES_CHANGED = 2;
+
+ /**
+ * Package name of print spooler.
+ *
+ * @hide
+ */
+ public static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+
+ /**
+ * Select enabled services.
+ *
+ * @see #getPrintServices
+ * @hide
+ */
+ public static final int ENABLED_SERVICES = 1 << 0;
+
+ /**
+ * Select disabled services.
+ *
+ * @see #getPrintServices
+ * @hide
+ */
+ public static final int DISABLED_SERVICES = 1 << 1;
+
+ /**
+ * Select all services.
+ *
+ * @see #getPrintServices
+ * @hide
+ */
+ public static final int ALL_SERVICES = ENABLED_SERVICES | DISABLED_SERVICES;
/**
* The action for launching the print dialog activity.
@@ -165,7 +198,10 @@ public final class PrintManager {
private final Handler mHandler;
- private Map mPrintJobStateChangeListeners;
+ private Map
+ mPrintJobStateChangeListeners;
+ private Map
+ mPrintServicesChangeListeners;
/** @hide */
public interface PrintJobStateChangeListener {
@@ -178,6 +214,15 @@ public final class PrintManager {
public void onPrintJobStateChanged(PrintJobId printJobId);
}
+ /** @hide */
+ public interface PrintServicesChangeListener {
+
+ /**
+ * Callback notifying that the print services changed.
+ */
+ public void onPrintServicesChanged();
+ }
+
/**
* Creates a new instance.
*
@@ -207,6 +252,15 @@ public final class PrintManager {
}
args.recycle();
} break;
+ case MSG_NOTIFY_PRINT_SERVICES_CHANGED: {
+ PrintServicesChangeListenerWrapper wrapper =
+ (PrintServicesChangeListenerWrapper) message.obj;
+ PrintServicesChangeListener listener = wrapper.getListener();
+ if (listener != null) {
+ listener.onPrintServicesChanged();
+ }
+ } break;
+
}
}
};
@@ -478,42 +532,82 @@ public final class PrintManager {
}
/**
- * Gets the list of enabled print services.
+ * Listen for changes to the installed and enabled print services.
*
- * @return The enabled service list or an empty list.
- * @hide
+ * @param listener the listener to add
+ *
+ * @see android.print.PrintManager#getPrintServices
*/
- public List getEnabledPrintServices() {
+ void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
if (mService == null) {
Log.w(LOG_TAG, "Feature android.software.print not available");
- return Collections.emptyList();
+ return;
}
+ if (mPrintServicesChangeListeners == null) {
+ mPrintServicesChangeListeners = new ArrayMap();
+ }
+ PrintServicesChangeListenerWrapper wrappedListener =
+ new PrintServicesChangeListenerWrapper(listener, mHandler);
try {
- List enabledServices = mService.getEnabledPrintServices(mUserId);
- if (enabledServices != null) {
- return enabledServices;
- }
+ mService.addPrintServicesChangeListener(wrappedListener, mUserId);
+ mPrintServicesChangeListeners.put(listener, wrappedListener);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- return Collections.emptyList();
}
/**
- * Gets the list of installed print services.
+ * Stop listening for changes to the installed and enabled print services.
*
- * @return The installed service list or an empty list.
- * @hide
+ * @param listener the listener to remove
+ *
+ * @see android.print.PrintManager#getPrintServices
*/
- public List getInstalledPrintServices() {
+ void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
if (mService == null) {
Log.w(LOG_TAG, "Feature android.software.print not available");
- return Collections.emptyList();
+ return;
}
+ if (mPrintServicesChangeListeners == null) {
+ return;
+ }
+ PrintServicesChangeListenerWrapper wrappedListener =
+ mPrintServicesChangeListeners.remove(listener);
+ if (wrappedListener == null) {
+ return;
+ }
+ if (mPrintServicesChangeListeners.isEmpty()) {
+ mPrintServicesChangeListeners = null;
+ }
+ wrappedListener.destroy();
try {
- List installedServices = mService.getInstalledPrintServices(mUserId);
- if (installedServices != null) {
- return installedServices;
+ mService.removePrintServicesChangeListener(wrappedListener, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error removing print services change listener", re);
+ }
+ }
+
+
+ /**
+ * Gets the list of print services, but does not register for updates. The user has to register
+ * for updates by itself, or use {@link PrintServicesLoader}.
+ *
+ * @param selectionFlags flags selecting which services to get. Either
+ * {@link #ENABLED_SERVICES},{@link #DISABLED_SERVICES}, or both.
+ *
+ * @return The enabled service list or an empty list.
+ *
+ * @see #addPrintServicesChangeListener(PrintServicesChangeListener)
+ * @see #removePrintServicesChangeListener(PrintServicesChangeListener)
+ *
+ * @hide
+ */
+ public @NonNull List getPrintServices(int selectionFlags) {
+ try {
+ List services = mService.getPrintServices(selectionFlags, mUserId);
+ if (services != null) {
+ return services;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -532,6 +626,26 @@ public final class PrintManager {
return new PrinterDiscoverySession(mService, mContext, mUserId);
}
+ /**
+ * Enable or disable a print service.
+ *
+ * @param service The service to enabled or disable
+ * @param isEnabled whether the service should be enabled or disabled
+ *
+ * @hide
+ */
+ public void setPrintServiceEnabled(@NonNull ComponentName service, boolean isEnabled) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
+ try {
+ mService.setPrintServiceEnabled(service, isEnabled, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error enabling or disabling " + service, re);
+ }
+ }
+
/**
* @hide
*/
@@ -1096,4 +1210,36 @@ public final class PrintManager {
return mWeakListener.get();
}
}
+
+ /**
+ * @hide
+ */
+ public static final class PrintServicesChangeListenerWrapper extends
+ IPrintServicesChangeListener.Stub {
+ private final WeakReference mWeakListener;
+ private final WeakReference mWeakHandler;
+
+ public PrintServicesChangeListenerWrapper(PrintServicesChangeListener listener,
+ Handler handler) {
+ mWeakListener = new WeakReference<>(listener);
+ mWeakHandler = new WeakReference<>(handler);
+ }
+
+ @Override
+ public void onPrintServicesChanged() {
+ Handler handler = mWeakHandler.get();
+ PrintServicesChangeListener listener = mWeakListener.get();
+ if (handler != null && listener != null) {
+ handler.obtainMessage(MSG_NOTIFY_PRINT_SERVICES_CHANGED, this).sendToTarget();
+ }
+ }
+
+ public void destroy() {
+ mWeakListener.clear();
+ }
+
+ public PrintServicesChangeListener getListener() {
+ return mWeakListener.get();
+ }
+ }
}
diff --git a/core/java/android/print/PrintServicesLoader.java b/core/java/android/print/PrintServicesLoader.java
new file mode 100644
index 0000000000000..ed411141d1fbd
--- /dev/null
+++ b/core/java/android/print/PrintServicesLoader.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 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.annotation.NonNull;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Handler;
+import android.os.Message;
+import android.printservice.PrintServiceInfo;
+
+import java.util.List;
+
+/**
+ * Loader for the list of print services. Can be parametrized to select a subset.
+ *
+ * @hide
+ */
+public class PrintServicesLoader extends Loader> {
+ /** What type of services to load. */
+ private final int mSelectionFlags;
+
+ /** The print manager to be used by this object */
+ private final @NonNull PrintManager mPrintManager;
+
+ /** Handler to sequentialize the delivery of the results to the main thread */
+ private Handler mHandler;
+
+ /** Listens for updates to the data from the platform */
+ private PrintManager.PrintServicesChangeListener mListener;
+
+ /**
+ * Create a new PrintServicesLoader.
+ *
+ * @param selectionFlags What type of services to load.
+ */
+ public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context,
+ int selectionFlags) {
+ super(context);
+ mPrintManager = printManager;
+ mSelectionFlags = selectionFlags;
+ }
+
+ @Override
+ protected void onForceLoad() {
+ queueNewResult();
+ }
+
+ /**
+ * Read the print services and queue it to be delivered on the main thread.
+ */
+ private void queueNewResult() {
+ Message m = mHandler.obtainMessage(0);
+ m.obj = mPrintManager.getPrintServices(mSelectionFlags);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ mHandler = new MyHandler();
+ mListener = new PrintManager.PrintServicesChangeListener() {
+ @Override public void onPrintServicesChanged() {
+ queueNewResult();
+ }
+ };
+
+ mPrintManager.addPrintServicesChangeListener(mListener);
+
+ // Immediately deliver a result
+ deliverResult(mPrintManager.getPrintServices(mSelectionFlags));
+ }
+
+ @Override
+ protected void onStopLoading() {
+ if (mListener != null) {
+ mPrintManager.removePrintServicesChangeListener(mListener);
+ mListener = null;
+ }
+
+ if (mHandler != null) {
+ mHandler.removeMessages(0);
+ mHandler = null;
+ }
+ }
+
+ @Override
+ protected void onReset() {
+ onStopLoading();
+ }
+
+ /**
+ * Handler to sequentialize all the updates to the main thread.
+ */
+ private class MyHandler extends Handler {
+ /**
+ * Create a new handler on the main thread.
+ */
+ public MyHandler() {
+ super(getContext().getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+
+ if (isStarted()) {
+ deliverResult((List) msg.obj);
+ }
+ }
+ }
+}
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 91e01f2dfd8ba..45e3d47c39dee 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -55,6 +55,8 @@ public final class PrintServiceInfo implements Parcelable {
private final String mId;
+ private boolean mIsEnabled;
+
private final ResolveInfo mResolveInfo;
private final String mSettingsActivityName;
@@ -70,6 +72,7 @@ public final class PrintServiceInfo implements Parcelable {
*/
public PrintServiceInfo(Parcel parcel) {
mId = parcel.readString();
+ mIsEnabled = parcel.readByte() != 0;
mResolveInfo = parcel.readParcelable(null);
mSettingsActivityName = parcel.readString();
mAddPrintersActivityName = parcel.readString();
@@ -179,6 +182,24 @@ public final class PrintServiceInfo implements Parcelable {
return mId;
}
+ /**
+ * If the service was enabled when it was read from the system.
+ *
+ * @return The id.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
+ * Mark a service as enabled or not
+ *
+ * @param isEnabled If the service should be marked as enabled.
+ */
+ public void setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ }
+
/**
* The service {@link ResolveInfo}.
*
@@ -238,6 +259,7 @@ public final class PrintServiceInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flagz) {
parcel.writeString(mId);
+ parcel.writeByte((byte)(mIsEnabled ? 1 : 0));
parcel.writeParcelable(mResolveInfo, 0);
parcel.writeString(mSettingsActivityName);
parcel.writeString(mAddPrintersActivityName);
@@ -276,6 +298,7 @@ public final class PrintServiceInfo implements Parcelable {
StringBuilder builder = new StringBuilder();
builder.append("PrintServiceInfo{");
builder.append("id=").append(mId);
+ builder.append("isEnabled=").append(mIsEnabled);
builder.append(", resolveInfo=").append(mResolveInfo);
builder.append(", settingsActivityName=").append(mSettingsActivityName);
builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName);
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
index 3feb0e905f1a8..c9bc8aaae0cb7 100644
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -61,7 +61,6 @@ import java.util.concurrent.TimeoutException;
public abstract class BasePrintTest extends InstrumentationTestCase {
private static final long OPERATION_TIMEOUT = 30000;
- private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
@@ -249,8 +248,9 @@ public abstract class BasePrintTest extends InstrumentationTestCase {
protected void clearPrintSpoolerData() throws Exception {
assertTrue("failed to clear print spooler data",
runShellCommand(getInstrumentation(), String.format(
- "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
- .contains(PM_CLEAR_SUCCESS_OUTPUT));
+ "pm clear --user %d %s", CURRENT_USER_ID,
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
+ .contains(PM_CLEAR_SUCCESS_OUTPUT));
}
@SuppressWarnings("unchecked")
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 5179b42fd650d..cbf17a43dbc6f 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -27,24 +27,9 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.print.IPrintDocumentAdapter;
-import android.print.IPrintJobStateChangeListener;
-import android.print.IPrintManager;
-import android.print.IPrinterDiscoveryObserver;
-import android.print.PageRange;
-import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintJob;
-import android.print.PrintJobId;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterCapabilitiesInfo;
-import android.print.PrinterDiscoverySession;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
import android.print.mockservice.MockPrintService;
@@ -134,7 +119,7 @@ public class IPrintManagerParametersTest extends BasePrintTest {
if (session.getPrinters().isEmpty()) {
final String PRINTER_NAME = "good printer";
- List printers = new ArrayList();
+ List printers = new ArrayList<>();
// Add the printer.
mGoodPrinterId = session.getService()
@@ -183,6 +168,18 @@ public class IPrintManagerParametersTest extends BasePrintTest {
new Handler(Looper.getMainLooper()));
}
+ /**
+ * Create a IPrintServicesChangeListener object.
+ *
+ * @return the object
+ * @throws Exception if the object could not be created.
+ */
+ private IPrintServicesChangeListener createMockIPrintServicesChangeListener() throws Exception {
+ return new PrintManager.PrintServicesChangeListenerWrapper(null,
+ new Handler(Looper.getMainLooper()));
+ }
+
+
/**
* Create a IPrinterDiscoveryObserver object.
*
@@ -193,6 +190,16 @@ public class IPrintManagerParametersTest extends BasePrintTest {
return new PrinterDiscoverySession.PrinterDiscoveryObserver(null);
}
+ private void startPrinting() {
+ mGoodPrintJob = print(createMockAdapter(), null);
+
+ // Wait for PrintActivity to be ready
+ waitForStartAdapterCallbackCalled();
+
+ // Wait for printer discovery session to be ready
+ waitForPrinterDiscoverySessionStartCallbackCalled();
+ }
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -201,20 +208,12 @@ public class IPrintManagerParametersTest extends BasePrintTest {
mGoodComponentName = getActivity().getComponentName();
- mGoodPrintJob = print(createMockAdapter(), null);
-
mIPrintManager = IPrintManager.Stub
.asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
// Generate dummy printerId which is a valid PrinterId object, but does not correspond to a
// printer
mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer");
-
- // Wait for PrintActivity to be ready
- waitForStartAdapterCallbackCalled();
-
- // Wait for printer discovery session to be ready
- waitForPrinterDiscoverySessionStartCallbackCalled();
}
/**
@@ -222,11 +221,11 @@ public class IPrintManagerParametersTest extends BasePrintTest {
*/
private interface Invokable {
/**
- * Execute the {@link Invokable}
+ * Execute the invokable
*
* @throws Exception
*/
- public void run() throws Exception;
+ void run() throws Exception;
}
/**
@@ -255,6 +254,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.getPrintJobInfo
*/
public void testGetPrintJobInfo() throws Exception {
+ startPrinting();
+
assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
mAppId, mUserId).getId());
assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId));
@@ -274,6 +275,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.getPrintJobInfos
*/
public void testGetPrintJobInfos() throws Exception {
+ startPrinting();
+
List infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
boolean foundPrintJob = false;
@@ -304,7 +307,7 @@ public class IPrintManagerParametersTest extends BasePrintTest {
final IPrintDocumentAdapter adapter = new PrintManager
.PrintDocumentAdapterDelegate(getActivity(), createMockAdapter());
- // Valid parameters are tested in setUp()
+ startPrinting();
assertException(new Invokable() {
@Override
@@ -352,6 +355,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.cancelPrintJob
*/
public void testCancelPrintJob() throws Exception {
+ startPrinting();
+
// Invalid print jobs IDs do not produce an exception
mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId);
mIPrintManager.cancelPrintJob(null, mAppId, mUserId);
@@ -373,6 +378,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.restartPrintJob
*/
public void testRestartPrintJob() throws Exception {
+ startPrinting();
+
mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
// Invalid print jobs IDs do not produce an exception
@@ -438,22 +445,103 @@ public class IPrintManagerParametersTest extends BasePrintTest {
}
/**
- * test IPrintManager.getInstalledPrintServices
+ * test IPrintManager.addPrintServicesChangeListener
*/
- public void testGetInstalledPrintServices() throws Exception {
- List printServices = mIPrintManager.getInstalledPrintServices(mUserId);
- assertTrue(printServices.size() >= 2);
+ public void testAddPrintServicesChangeListener() throws Exception {
+ final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
+
+ mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.addPrintServicesChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
}
/**
- * test IPrintManager.getEnabledPrintServices
+ * test IPrintManager.removePrintServicesChangeListener
*/
- public void testGetEnabledPrintServices() throws Exception {
- List printServices = mIPrintManager.getEnabledPrintServices(mUserId);
+ public void testRemovePrintServicesChangeListener() throws Exception {
+ final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
+
+ mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+ mIPrintManager.removePrintServicesChangeListener(listener, mUserId);
+
+ // Removing unknown listeners is a no-op
+ mIPrintManager.removePrintServicesChangeListener(listener, mUserId);
+
+ mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.removePrintServicesChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getPrintServices
+ */
+ public void testGetPrintServices() throws Exception {
+ List printServices = mIPrintManager.getPrintServices(
+ PrintManager.ALL_SERVICES, mUserId);
assertTrue(printServices.size() >= 2);
+ printServices = mIPrintManager.getPrintServices(0, mUserId);
+ assertEquals(printServices, null);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.getPrintServices(~PrintManager.ALL_SERVICES, mUserId);
+ }
+ }, IllegalArgumentException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.setPrintServiceEnabled
+ */
+ public void testSetPrintServiceEnabled() throws Exception {
+ final ComponentName printService = mIPrintManager.getPrintServices(
+ PrintManager.ALL_SERVICES, mUserId).get(0).getComponentName();
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(printService, false, mUserId);
+ }
+ }, SecurityException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(printService, true, mUserId);
+ }
+ }, SecurityException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(new ComponentName("bad", "name"), true,
+ mUserId);
+ }
+ }, SecurityException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(null, true, mUserId);
+ }
+ }, SecurityException.class);
+
// Cannot test bad user Id as these tests are allowed to call across users
}
@@ -486,6 +574,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.startPrinterDiscovery
*/
public void testStartPrinterDiscovery() throws Exception {
+ startPrinting();
+
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
final List goodPrinters = new ArrayList<>();
goodPrinters.add(mGoodPrinterId);
@@ -549,6 +639,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.validatePrinters
*/
public void testValidatePrinters() throws Exception {
+ startPrinting();
+
final List goodPrinters = new ArrayList<>();
goodPrinters.add(mGoodPrinterId);
@@ -587,6 +679,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.startPrinterStateTracking
*/
public void testStartPrinterStateTracking() throws Exception {
+ startPrinting();
+
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
// Bad printers do no cause exceptions
@@ -606,6 +700,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.getCustomPrinterIcon
*/
public void testGetCustomPrinterIcon() throws Exception {
+ startPrinting();
+
mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
// Bad printers do no cause exceptions
@@ -625,6 +721,8 @@ public class IPrintManagerParametersTest extends BasePrintTest {
* test IPrintManager.stopPrinterStateTracking
*/
public void testStopPrinterStateTracking() throws Exception {
+ startPrinting();
+
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index af9d25196bec5..1bdb6d8bc4a7c 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -63,7 +63,7 @@
android:name=".ui.PrintActivity"
android:configChanges="screenSize|smallestScreenSize|orientation"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
- android:theme="@style/PrintActivity">
+ android:theme="@style/Theme.PrintActivity">
@@ -74,7 +74,14 @@
+
+
+
diff --git a/packages/PrintSpooler/res/drawable/ic_add.xml b/packages/PrintSpooler/res/drawable/ic_add.xml
index 1442b1b61f467..f728e7d0668e8 100644
--- a/packages/PrintSpooler/res/drawable/ic_add.xml
+++ b/packages/PrintSpooler/res/drawable/ic_add.xml
@@ -21,5 +21,5 @@
android:viewportHeight="24.0">
+ android:fillColor="?android:attr/colorAccent"/>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/ic_print.xml b/packages/PrintSpooler/res/drawable/ic_print.xml
index dc6e0fbc1d23c..e5e4d075f0192 100644
--- a/packages/PrintSpooler/res/drawable/ic_print.xml
+++ b/packages/PrintSpooler/res/drawable/ic_print.xml
@@ -16,4 +16,4 @@
+ android:tint="?android:attr/colorAccent" />
diff --git a/packages/PrintSpooler/res/drawable/page_selector_background.xml b/packages/PrintSpooler/res/drawable/page_selector_background.xml
index 7f1da314e1eee..4d32328efdc90 100644
--- a/packages/PrintSpooler/res/drawable/page_selector_background.xml
+++ b/packages/PrintSpooler/res/drawable/page_selector_background.xml
@@ -22,14 +22,14 @@
android:state_selected="true">
+ android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorAccent">
diff --git a/packages/PrintSpooler/res/drawable/print_button_background.xml b/packages/PrintSpooler/res/drawable/print_button_background.xml
index aec84744af13c..ad16547252aeb 100644
--- a/packages/PrintSpooler/res/drawable/print_button_background.xml
+++ b/packages/PrintSpooler/res/drawable/print_button_background.xml
@@ -18,7 +18,7 @@
android:shape="oval">
+ android:color="?android:attr/colorAccent">
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_header.xml b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
new file mode 100644
index 0000000000000..ff342cbbb0fa1
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_item.xml b/packages/PrintSpooler/res/layout/add_printer_list_item.xml
new file mode 100644
index 0000000000000..1a2e774d2474b
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_list_item.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/layout/all_print_services_list_item.xml b/packages/PrintSpooler/res/layout/all_print_services_list_item.xml
new file mode 100644
index 0000000000000..a2471c64b56e9
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/all_print_services_list_item.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml b/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
new file mode 100644
index 0000000000000..73d09333bf808
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml b/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
new file mode 100644
index 0000000000000..c13487a4851a1
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index defbf8db0026b..103c157b873f1 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -16,10 +16,8 @@
@@ -33,10 +31,9 @@
android:visibility="invisible">
-
@@ -47,8 +44,6 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
- android:gravity="top|start"
android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
@@ -57,15 +52,15 @@
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_below="@id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
android:visibility="gone"
android:textColor="?android:attr/textColorSecondary"
android:duplicateParentState="true">
-
+
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
index 11fef2d21fde3..60f7088346286 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
@@ -16,13 +16,10 @@
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
index 1209aa6f0fa2d..0784bab589bc6 100644
--- a/packages/PrintSpooler/res/layout/printer_list_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -16,10 +16,9 @@
@@ -28,18 +27,15 @@
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="center_vertical"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="8dip"
android:duplicateParentState="true"
android:contentDescription="@null"
android:visibility="invisible">
@@ -47,15 +43,9 @@
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:fadingEdge="horizontal"
- android:textAlignment="viewStart"
- android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
@@ -64,30 +54,31 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
- android:layout_alignParentStart="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
android:visibility="gone"
- android:textColor="?android:attr/textColorSecondary"
- android:textAlignment="viewStart"
android:duplicateParentState="true">
-
+
-
+
+
+
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 77c500ab86b20..564802ae11ab8 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -22,11 +22,7 @@
-
+ android:layout_height="fill_parent" />
+ style="?android:attr/progressBarStyleHorizontal">
+
+
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index 15cc13939cf0e..60dfdcaf19649 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -26,12 +26,4 @@
android:imeOptions="actionSearch">
-
-
-
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index d1bec32cf1bb7..47e616ef40c77 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -22,8 +22,6 @@
#F2F1F2
- #FF80CBC4
-
#ffa3a3a3
diff --git a/packages/PrintSpooler/res/values/donottranslate.xml b/packages/PrintSpooler/res/values/donottranslate.xml
index 8069a1da89b32..589043b02517a 100644
--- a/packages/PrintSpooler/res/values/donottranslate.xml
+++ b/packages/PrintSpooler/res/values/donottranslate.xml
@@ -25,4 +25,6 @@
ISO_A4@string/mediasize_standard_iso
+ market://details?id=%1$s
+
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 76292a16e9b14..4b566221e4fad 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -129,7 +129,7 @@
Search box hidden
-
+
Add printer
@@ -151,12 +151,7 @@
More information about this printer
- Some print services are disabled.
-
-
-
-
- Choose print service
+ Some print services are disabledSearching for printers
@@ -167,6 +162,29 @@
No printers found
+
+
+
+ Cannot add printers
+
+
+ Select to add printer
+
+
+ Select to enable
+
+
+ Enabled services
+
+
+ Recommended services
+
+
+ Disabled services
+
+
+ All services
+
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
new file mode 100644
index 0000000000000..1e63a67e50a44
--- /dev/null
+++ b/packages/PrintSpooler/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 05de5b79a6f9b..a968ffa9ed1d6 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -15,8 +15,17 @@
-->
+
-
+
+