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"> +