Merge "Add "app printer activity" and always keep the print service state updated. Also fiddle with the UI to use more standard values." into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
02a465ace7
@@ -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 \
|
||||
|
||||
@@ -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<PrintServiceInfo> getInstalledPrintServices(int userId);
|
||||
List<PrintServiceInfo> 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<PrintServiceInfo> 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,
|
||||
|
||||
26
core/java/android/print/IPrintServicesChangeListener.aidl
Normal file
26
core/java/android/print/IPrintServicesChangeListener.aidl
Normal file
@@ -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();
|
||||
}
|
||||
@@ -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.
|
||||
* </p>
|
||||
* @see #getPrintServices
|
||||
* @hide
|
||||
*/
|
||||
public static final int ENABLED_SERVICES = 1 << 0;
|
||||
|
||||
/**
|
||||
* Select disabled services.
|
||||
* </p>
|
||||
* @see #getPrintServices
|
||||
* @hide
|
||||
*/
|
||||
public static final int DISABLED_SERVICES = 1 << 1;
|
||||
|
||||
/**
|
||||
* Select all services.
|
||||
* </p>
|
||||
* @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<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper> mPrintJobStateChangeListeners;
|
||||
private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>
|
||||
mPrintJobStateChangeListeners;
|
||||
private Map<PrintServicesChangeListener, PrintServicesChangeListenerWrapper>
|
||||
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<PrintServiceInfo> 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<PrintServicesChangeListener,
|
||||
PrintServicesChangeListenerWrapper>();
|
||||
}
|
||||
PrintServicesChangeListenerWrapper wrappedListener =
|
||||
new PrintServicesChangeListenerWrapper(listener, mHandler);
|
||||
try {
|
||||
List<PrintServiceInfo> 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<PrintServiceInfo> 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<PrintServiceInfo> 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<PrintServiceInfo> getPrintServices(int selectionFlags) {
|
||||
try {
|
||||
List<PrintServiceInfo> 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<PrintServicesChangeListener> mWeakListener;
|
||||
private final WeakReference<Handler> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
125
core/java/android/print/PrintServicesLoader.java
Normal file
125
core/java/android/print/PrintServicesLoader.java
Normal file
@@ -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<List<PrintServiceInfo>> {
|
||||
/** 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<PrintServiceInfo>) msg.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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<PrinterInfo> printers = new ArrayList<PrinterInfo>();
|
||||
List<PrinterInfo> 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<PrintJobInfo> 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<PrintServiceInfo> 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<PrintServiceInfo> 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<PrintServiceInfo> 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<PrinterId> 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<PrinterId> 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);
|
||||
|
||||
|
||||
@@ -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">
|
||||
<intent-filter>
|
||||
<action android:name="android.print.PRINT_DIALOG" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -74,7 +74,14 @@
|
||||
<activity
|
||||
android:name=".ui.SelectPrinterActivity"
|
||||
android:label="@string/all_printers_label"
|
||||
android:theme="@android:style/Theme.Material.Settings"
|
||||
android:theme="@style/Theme.SelectPrinterActivity"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.AddPrinterActivity"
|
||||
android:label="@string/print_add_printer"
|
||||
android:theme="@style/Theme.AddPrinterActivity"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
android:fillColor="?android:attr/colorAccent"/>
|
||||
</vector>
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@*android:drawable/ic_print"
|
||||
android:tint="@color/promoted_action_background_color" />
|
||||
android:tint="?android:attr/colorAccent" />
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
android:state_selected="true">
|
||||
<bitmap
|
||||
android:src="@drawable/ic_check_circle"
|
||||
android:tint="@color/promoted_action_background_color">
|
||||
android:tint="?android:attr/colorAccent">
|
||||
</bitmap>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<bitmap
|
||||
android:src="@drawable/ic_remove_circle"
|
||||
android:tint="@color/promoted_action_background_color">
|
||||
android:tint="?android:attr/colorAccent">
|
||||
</bitmap>
|
||||
</item>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
android:shape="oval">
|
||||
|
||||
<solid
|
||||
android:color="@color/promoted_action_background_color">
|
||||
android:color="?android:attr/colorAccent">
|
||||
</solid>
|
||||
|
||||
<size
|
||||
|
||||
29
packages/PrintSpooler/res/layout/add_printer_activity.xml
Normal file
29
packages/PrintSpooler/res/layout/add_printer_activity.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="16dip"
|
||||
android:paddingBottom="16dip">
|
||||
|
||||
<ListView android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-16dip"
|
||||
android:id="@android:id/list" />
|
||||
|
||||
</LinearLayout>
|
||||
28
packages/PrintSpooler/res/layout/add_printer_list_header.xml
Normal file
28
packages/PrintSpooler/res/layout/add_printer_list_header.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
<TextView android:id="@+id/text"
|
||||
style="?android:attr/listSeparatorTextViewStyle" />
|
||||
|
||||
</LinearLayout>
|
||||
46
packages/PrintSpooler/res/layout/add_printer_list_item.xml
Normal file
46
packages/PrintSpooler/res/layout/add_printer_list_item.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@null"
|
||||
android:layout_marginEnd="16dip"
|
||||
android:src="@drawable/ic_add" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dip">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/print_add_printer" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="56dip">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/all_services_title" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="40dip"
|
||||
android:layout_height="40dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@null" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:text="@string/enable_print_service" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="40dip"
|
||||
android:layout_height="40dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@null" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dip"
|
||||
android:paddingEnd="8dip"
|
||||
android:minHeight="56dip"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
style="?android:attr/spinnerItemStyle"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
@@ -33,10 +31,9 @@
|
||||
android:visibility="invisible">
|
||||
</ImageView>
|
||||
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="8dip"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
@@ -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">
|
||||
</TextView>
|
||||
@@ -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">
|
||||
</TextView>
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textIsSelectable="false"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:paddingStart="20dip"
|
||||
android:paddingEnd="8dip"
|
||||
android:minHeight="56dip"
|
||||
android:orientation="horizontal"
|
||||
style="?android:attr/spinnerItemStyle"
|
||||
android:text="@string/destination_default_text"
|
||||
android:gravity="start|center_vertical" />
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
@@ -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">
|
||||
</ImageView>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="16dip"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
@@ -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">
|
||||
</TextView>
|
||||
|
||||
@@ -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">
|
||||
</TextView>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView
|
||||
<!-- wrapper for image view to increase the touch target size -->
|
||||
<LinearLayout
|
||||
android:id="@+id/more_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingLeft="16dip"
|
||||
android:contentDescription="@string/printer_info_desc"
|
||||
android:src="@drawable/ic_info"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:tintMode="src_in"
|
||||
android:layout_height="fill_parent"
|
||||
android:visibility="gone">
|
||||
</ImageView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingLeft="16dip"
|
||||
android:contentDescription="@string/printer_info_desc"
|
||||
android:src="@drawable/ic_info"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:tintMode="src_in" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
android:cacheColorHint="@android:color/transparent"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="true" >
|
||||
</ListView>
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/empty_print_state"
|
||||
@@ -63,9 +59,18 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal">
|
||||
style="?android:attr/progressBarStyleHorizontal">
|
||||
</ProgressBar>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/print_add_printer"
|
||||
android:textAllCaps="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@@ -26,12 +26,4 @@
|
||||
android:imeOptions="actionSearch">
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_add_printer"
|
||||
android:title="@string/print_add_printer"
|
||||
android:icon="@drawable/ic_add"
|
||||
android:showAsAction="ifRoom"
|
||||
android:alphabeticShortcut="a">
|
||||
</item>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
|
||||
<color name="print_preview_background_color">#F2F1F2</color>
|
||||
|
||||
<color name="promoted_action_background_color">#FF80CBC4</color>
|
||||
|
||||
<color name="material_grey_500">#ffa3a3a3</color>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -25,4 +25,6 @@
|
||||
<string name="mediasize_default">ISO_A4</string>
|
||||
<string name="mediasize_standard">@string/mediasize_standard_iso</string>
|
||||
|
||||
<string name="uri_package_details">market://details?id=%1$s</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
<!-- Utterance to announce that the search box is hidden. This is spoken to a blind user. [CHAR LIMIT=none] -->
|
||||
<string name="print_search_box_hidden_utterance">Search box hidden</string>
|
||||
|
||||
<!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
|
||||
<!-- Label of add printers button when no printers are found. [CHAR LIMIT=25] -->
|
||||
<string name="print_add_printer">Add printer</string>
|
||||
|
||||
<!-- Title of the menu item to select a printer. [CHAR LIMIT=25] -->
|
||||
@@ -151,12 +151,7 @@
|
||||
<string name="printer_info_desc">More information about this printer</string>
|
||||
|
||||
<!-- Notification that print services as disabled. [CHAR LIMIT=50] -->
|
||||
<string name="print_services_disabled_toast">Some print services are disabled.</string>
|
||||
|
||||
<!-- Add printer dialog -->
|
||||
|
||||
<!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
|
||||
<string name="choose_print_service">Choose print service</string>
|
||||
<string name="print_services_disabled_toast">Some print services are disabled</string>
|
||||
|
||||
<!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
|
||||
<string name="print_searching_for_printers">Searching for printers</string>
|
||||
@@ -167,6 +162,29 @@
|
||||
<!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
|
||||
<string name="print_no_printers">No printers found</string>
|
||||
|
||||
<!-- Add printer activity -->
|
||||
|
||||
<!-- Subtitle for services that cannot add printers. [CHAR LIMIT=50] -->
|
||||
<string name="cannot_add_printer">Cannot add printers</string>
|
||||
|
||||
<!-- Subtitle for services that can add printers. [CHAR LIMIT=50] -->
|
||||
<string name="select_to_add_printers">Select to add printer</string>
|
||||
|
||||
<!-- Subtitle for disabled services. [CHAR LIMIT=50] -->
|
||||
<string name="enable_print_service">Select to enable</string>
|
||||
|
||||
<!-- Header for the list of enabled print services. [CHAR LIMIT=50] -->
|
||||
<string name="enabled_services_title">Enabled services</string>
|
||||
|
||||
<!-- Header for the list of recommended print services. [CHAR LIMIT=50] -->
|
||||
<string name="recommended_services_title">Recommended services</string>
|
||||
|
||||
<!-- Header for the list of disabled print services. [CHAR LIMIT=50] -->
|
||||
<string name="disabled_services_title">Disabled services</string>
|
||||
|
||||
<!-- Label for the list item that links to the list of all print services. [CHAR LIMIT=50] -->
|
||||
<string name="all_services_title">All services</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
|
||||
<!-- Template for the notification label for a printing print job. [CHAR LIMIT=25] -->
|
||||
|
||||
34
packages/PrintSpooler/res/values/styles.xml
Normal file
34
packages/PrintSpooler/res/values/styles.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- Preference styles -->
|
||||
<eat-comment/>
|
||||
|
||||
<style name="ListItemSecondary" parent="@android:style/TextAppearance.Material.Body1">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="ListSeparator">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">16dip</item>
|
||||
<item name="android:layout_marginBottom">16dip</item>
|
||||
<item name="android:textColor">?android:attr/colorAccent</item>
|
||||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -15,8 +15,17 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<style name="Theme.AddPrinterActivity" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
|
||||
<item name="android:listSeparatorTextViewStyle">@style/ListSeparator</item>
|
||||
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="PrintActivity" parent="@android:style/Theme.DeviceDefault">
|
||||
<style name="Theme.SelectPrinterActivity"
|
||||
parent="android:style/Theme.DeviceDefault.Light.DarkActionBar">
|
||||
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
|
||||
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* 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 com.android.printspooler.ui;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ListActivity;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.database.DataSetObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrintServicesLoader;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.android.printspooler.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is an activity for adding a printer or. It consists of a list fed from three adapters:
|
||||
* <ul>
|
||||
* <li>{@link #mEnabledServicesAdapter} for all enabled services. If a service has an {@link
|
||||
* PrintServiceInfo#getAddPrintersActivityName() add printer activity} this is started
|
||||
* when the item is clicked.</li>
|
||||
* <li>{@link #mDisabledServicesAdapter} for all disabled services. Once clicked the settings page
|
||||
* for this service is opened.</li>
|
||||
* <li>{@link RecommendedServicesAdapter} for a link to all services. If this item is clicked
|
||||
* the market app is opened to show all print services.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class AddPrinterActivity extends ListActivity implements
|
||||
LoaderManager.LoaderCallbacks<List<PrintServiceInfo>>,
|
||||
AdapterView.OnItemClickListener {
|
||||
private static final String LOG_TAG = "AddPrinterActivity";
|
||||
|
||||
/** Ids for the loaders */
|
||||
private static final int LOADER_ID_ENABLED_SERVICES = 1;
|
||||
private static final int LOADER_ID_DISABLED_SERVICES = 2;
|
||||
|
||||
/**
|
||||
* The enabled services list. This is filled from the {@link #LOADER_ID_ENABLED_SERVICES}
|
||||
* loader in {@link #onLoadFinished}.
|
||||
*/
|
||||
private EnabledServicesAdapter mEnabledServicesAdapter;
|
||||
|
||||
/**
|
||||
* The disabled services list. This is filled from the {@link #LOADER_ID_DISABLED_SERVICES}
|
||||
* loader in {@link #onLoadFinished}.
|
||||
*/
|
||||
private DisabledServicesAdapter mDisabledServicesAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.add_printer_activity);
|
||||
|
||||
mEnabledServicesAdapter = new EnabledServicesAdapter();
|
||||
mDisabledServicesAdapter = new DisabledServicesAdapter();
|
||||
|
||||
ArrayList<ActionAdapter> adapterList = new ArrayList<>(3);
|
||||
adapterList.add(mEnabledServicesAdapter);
|
||||
adapterList.add(new RecommendedServicesAdapter());
|
||||
adapterList.add(mDisabledServicesAdapter);
|
||||
|
||||
setListAdapter(new CombinedAdapter(adapterList));
|
||||
|
||||
getListView().setOnItemClickListener(this);
|
||||
|
||||
getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, this);
|
||||
getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, this);
|
||||
// TODO: Load recommended services
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
|
||||
switch (id) {
|
||||
case LOADER_ID_ENABLED_SERVICES:
|
||||
return new PrintServicesLoader(
|
||||
(PrintManager) getSystemService(Context.PRINT_SERVICE), this,
|
||||
PrintManager.ENABLED_SERVICES);
|
||||
case LOADER_ID_DISABLED_SERVICES:
|
||||
return new PrintServicesLoader(
|
||||
(PrintManager) getSystemService(Context.PRINT_SERVICE), this,
|
||||
PrintManager.DISABLED_SERVICES);
|
||||
// TODO: Load recommended services
|
||||
default:
|
||||
// not reached
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
((ActionAdapter) getListAdapter()).performAction(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
|
||||
List<PrintServiceInfo> data) {
|
||||
switch (loader.getId()) {
|
||||
case LOADER_ID_ENABLED_SERVICES:
|
||||
mEnabledServicesAdapter.updateData(data);
|
||||
break;
|
||||
case LOADER_ID_DISABLED_SERVICES:
|
||||
mDisabledServicesAdapter.updateData(data);
|
||||
break;
|
||||
// TODO: Load recommended services
|
||||
default:
|
||||
// not reached
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
|
||||
if (!isFinishing()) {
|
||||
switch (loader.getId()) {
|
||||
case LOADER_ID_ENABLED_SERVICES:
|
||||
mEnabledServicesAdapter.updateData(null);
|
||||
break;
|
||||
case LOADER_ID_DISABLED_SERVICES:
|
||||
mDisabledServicesAdapter.updateData(null);
|
||||
break;
|
||||
// TODO: Reset recommended services
|
||||
default:
|
||||
// not reached
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an adapter that can can perform an action for a position in it's list.
|
||||
*/
|
||||
private abstract class ActionAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Perform the action for a position in the list.
|
||||
*
|
||||
* @param position The position of the item
|
||||
*/
|
||||
abstract void performAction(@IntRange(from = 0) int position);
|
||||
|
||||
@Override
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An adapter presenting multiple sub adapters as a single combined adapter.
|
||||
*/
|
||||
private class CombinedAdapter extends ActionAdapter {
|
||||
/** The adapters to combine */
|
||||
private final @NonNull ArrayList<ActionAdapter> mAdapters;
|
||||
|
||||
/**
|
||||
* Create a combined adapter.
|
||||
*
|
||||
* @param adapters the list of adapters to combine
|
||||
*/
|
||||
CombinedAdapter(@NonNull ArrayList<ActionAdapter> adapters) {
|
||||
mAdapters = adapters;
|
||||
|
||||
final int numAdapters = mAdapters.size();
|
||||
for (int i = 0; i < numAdapters; i++) {
|
||||
mAdapters.get(i).registerDataSetObserver(new DataSetObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
int totalCount = 0;
|
||||
|
||||
final int numAdapters = mAdapters.size();
|
||||
for (int i = 0; i < numAdapters; i++) {
|
||||
totalCount += mAdapters.get(i).getCount();
|
||||
}
|
||||
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the sub adapter and the position in the sub-adapter the position in the combined
|
||||
* adapter refers to.
|
||||
*
|
||||
* @param position The position in the combined adapter
|
||||
*
|
||||
* @return The pair of adapter and position in sub adapter
|
||||
*/
|
||||
private @NonNull Pair<ActionAdapter, Integer> getSubAdapter(int position) {
|
||||
final int numAdapters = mAdapters.size();
|
||||
for (int i = 0; i < numAdapters; i++) {
|
||||
ActionAdapter adapter = mAdapters.get(i);
|
||||
|
||||
if (position < adapter.getCount()) {
|
||||
return new Pair<>(adapter, position);
|
||||
} else {
|
||||
position -= adapter.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid position");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
int numLowerViewTypes = 0;
|
||||
|
||||
final int numAdapters = mAdapters.size();
|
||||
for (int i = 0; i < numAdapters; i++) {
|
||||
Adapter adapter = mAdapters.get(i);
|
||||
|
||||
if (position < adapter.getCount()) {
|
||||
return numLowerViewTypes + adapter.getItemViewType(position);
|
||||
} else {
|
||||
numLowerViewTypes += adapter.getViewTypeCount();
|
||||
position -= adapter.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid position");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
int totalViewCount = 0;
|
||||
|
||||
final int numAdapters = mAdapters.size();
|
||||
for (int i = 0; i < numAdapters; i++) {
|
||||
totalViewCount += mAdapters.get(i).getViewTypeCount();
|
||||
}
|
||||
|
||||
return totalViewCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
|
||||
|
||||
return realPosition.first.getView(realPosition.second, convertView, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
|
||||
|
||||
return realPosition.first.getItem(realPosition.second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
|
||||
|
||||
return realPosition.first.isEnabled(realPosition.second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction(@IntRange(from = 0) int position) {
|
||||
Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
|
||||
|
||||
realPosition.first.performAction(realPosition.second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Superclass for all adapters that just display a list of {@link PrintServiceInfo}.
|
||||
*/
|
||||
private abstract class PrintServiceInfoAdapter extends ActionAdapter {
|
||||
/**
|
||||
* Raw data of the list.
|
||||
*
|
||||
* @see #updateData(List)
|
||||
*/
|
||||
private @NonNull List<PrintServiceInfo> mServices;
|
||||
|
||||
/**
|
||||
* Create a new adapter.
|
||||
*/
|
||||
PrintServiceInfoAdapter() {
|
||||
mServices = Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data.
|
||||
*
|
||||
* @param services The new raw data.
|
||||
*/
|
||||
void updateData(@Nullable List<PrintServiceInfo> services) {
|
||||
if (services == null || services.isEmpty()) {
|
||||
mServices = Collections.emptyList();
|
||||
} else {
|
||||
mServices = services;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mServices.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
return mServices.size() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (position == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return mServices.get(position - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
return position != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for the enabled services.
|
||||
*/
|
||||
private class EnabledServicesAdapter extends PrintServiceInfoAdapter {
|
||||
@Override
|
||||
public void performAction(@IntRange(from = 0) int position) {
|
||||
PrintServiceInfo service = (PrintServiceInfo) getItem(position);
|
||||
String addPrinterActivityName = service.getAddPrintersActivityName();
|
||||
|
||||
if (!TextUtils.isEmpty(addPrinterActivityName)) {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(new ComponentName(service.getComponentName().getPackageName(),
|
||||
addPrinterActivityName));
|
||||
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Cannot start add printers activity", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (position == 0) {
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
((TextView) convertView.findViewById(R.id.text))
|
||||
.setText(R.string.enabled_services_title);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.enabled_print_services_list_item,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
PrintServiceInfo service = (PrintServiceInfo) getItem(position);
|
||||
|
||||
TextView title = (TextView) convertView.findViewById(R.id.title);
|
||||
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
|
||||
TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle);
|
||||
|
||||
title.setText(service.getResolveInfo().loadLabel(getPackageManager()));
|
||||
icon.setImageDrawable(service.getResolveInfo().loadIcon(getPackageManager()));
|
||||
|
||||
if (TextUtils.isEmpty(service.getAddPrintersActivityName())) {
|
||||
subtitle.setText(getString(R.string.cannot_add_printer));
|
||||
} else {
|
||||
subtitle.setText(getString(R.string.select_to_add_printers));
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for the disabled services.
|
||||
*/
|
||||
private class DisabledServicesAdapter extends PrintServiceInfoAdapter {
|
||||
@Override
|
||||
public void performAction(@IntRange(from = 0) int position) {
|
||||
((PrintManager) getSystemService(Context.PRINT_SERVICE)).setPrintServiceEnabled(
|
||||
((PrintServiceInfo) getItem(position)).getComponentName(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (position == 0) {
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
((TextView) convertView.findViewById(R.id.text))
|
||||
.setText(R.string.disabled_services_title);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(
|
||||
R.layout.disabled_print_services_list_item, parent, false);
|
||||
}
|
||||
|
||||
PrintServiceInfo service = (PrintServiceInfo) getItem(position);
|
||||
|
||||
TextView title = (TextView) convertView.findViewById(R.id.title);
|
||||
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
|
||||
|
||||
title.setText(service.getResolveInfo().loadLabel(getPackageManager()));
|
||||
icon.setImageDrawable(service.getResolveInfo().loadIcon(getPackageManager()));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for the recommended services.
|
||||
*/
|
||||
private class RecommendedServicesAdapter extends ActionAdapter {
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (position == 0) {
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
((TextView) convertView.findViewById(R.id.text))
|
||||
.setText(R.string.recommended_services_title);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.all_print_services_list_item,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
return position != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction(@IntRange(from = 0) int position) {
|
||||
String searchUri = Settings.Secure
|
||||
.getString(getContentResolver(), Settings.Secure.PRINT_SERVICE_SEARCH_URI);
|
||||
|
||||
if (searchUri != null) {
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Cannot start market", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.android.printspooler.ui;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Loader;
|
||||
@@ -28,9 +31,11 @@ import android.location.LocationManager;
|
||||
import android.location.LocationRequest;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrintServicesLoader;
|
||||
import android.print.PrinterDiscoverySession;
|
||||
import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
|
||||
import android.print.PrinterId;
|
||||
@@ -127,11 +132,11 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
}
|
||||
}
|
||||
|
||||
public FusedPrintersProvider(Context context) {
|
||||
super(context);
|
||||
public FusedPrintersProvider(Activity activity, int internalLoaderId) {
|
||||
super(activity);
|
||||
mLocationLock = new Object();
|
||||
mPersistenceManager = new PersistenceManager(context);
|
||||
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
mPersistenceManager = new PersistenceManager(activity, internalLoaderId);
|
||||
mLocationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
|
||||
public void addHistoricalPrinter(PrinterInfo printer) {
|
||||
@@ -383,7 +388,6 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
mPrinters.clear();
|
||||
if (mDiscoverySession != null) {
|
||||
mDiscoverySession.destroy();
|
||||
mDiscoverySession = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,7 +503,8 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters, getCurrentLocation());
|
||||
}
|
||||
|
||||
private final class PersistenceManager {
|
||||
private final class PersistenceManager implements
|
||||
LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
|
||||
private static final String PERSIST_FILE_NAME = "printer_history.xml";
|
||||
|
||||
private static final String TAG_PRINTERS = "printers";
|
||||
@@ -520,6 +525,15 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
|
||||
private final AtomicFile mStatePersistFile;
|
||||
|
||||
/**
|
||||
* Whether the enabled print services have been updated since last time the history was
|
||||
* read.
|
||||
*/
|
||||
private boolean mAreEnabledServicesUpdated;
|
||||
|
||||
/** The enabled services read when they were last updated */
|
||||
private @NonNull List<PrintServiceInfo> mEnabledServices;
|
||||
|
||||
private List<Pair<PrinterInfo, Location>> mHistoricalPrinters = new ArrayList<>();
|
||||
|
||||
private boolean mReadHistoryCompleted;
|
||||
@@ -528,9 +542,52 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
|
||||
private volatile long mLastReadHistoryTimestamp;
|
||||
|
||||
private PersistenceManager(Context context) {
|
||||
mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
|
||||
private PersistenceManager(final Activity activity, final int internalLoaderId) {
|
||||
mStatePersistFile = new AtomicFile(new File(activity.getFilesDir(),
|
||||
PERSIST_FILE_NAME));
|
||||
|
||||
// Initialize enabled services to make sure they are set are the read task might be done
|
||||
// before the loader updated the services the first time.
|
||||
mEnabledServices = ((PrintManager) activity
|
||||
.getSystemService(Context.PRINT_SERVICE))
|
||||
.getPrintServices(PrintManager.ENABLED_SERVICES);
|
||||
|
||||
mAreEnabledServicesUpdated = true;
|
||||
|
||||
// Cannot start a loader while starting another, hence delay this loader
|
||||
(new Handler(activity.getMainLooper())).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getLoaderManager().initLoader(internalLoaderId, null,
|
||||
PersistenceManager.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
|
||||
return new PrintServicesLoader(
|
||||
(PrintManager) getContext().getSystemService(Context.PRINT_SERVICE),
|
||||
getContext(), PrintManager.ENABLED_SERVICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
|
||||
List<PrintServiceInfo> services) {
|
||||
mAreEnabledServicesUpdated = true;
|
||||
mEnabledServices = services;
|
||||
|
||||
// Ask the fused printer provider to reload which will cause the persistence manager to
|
||||
// reload the history and reconsider the enabled services.
|
||||
if (isStarted()) {
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
|
||||
// no data is cached
|
||||
}
|
||||
|
||||
public boolean isReadHistoryInProgress() {
|
||||
@@ -644,7 +701,8 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
}
|
||||
|
||||
public boolean isHistoryChanged() {
|
||||
return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
|
||||
return mAreEnabledServicesUpdated ||
|
||||
mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,19 +796,15 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
|
||||
}
|
||||
|
||||
// Ignore printer records whose target services are not enabled.
|
||||
PrintManager printManager = (PrintManager) getContext()
|
||||
.getSystemService(Context.PRINT_SERVICE);
|
||||
List<PrintServiceInfo> services = printManager
|
||||
.getEnabledPrintServices();
|
||||
|
||||
Set<ComponentName> enabledComponents = new ArraySet<>();
|
||||
final int installedServiceCount = services.size();
|
||||
final int installedServiceCount = mEnabledServices.size();
|
||||
for (int i = 0; i < installedServiceCount; i++) {
|
||||
ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
|
||||
ServiceInfo serviceInfo = mEnabledServices.get(i).getResolveInfo().serviceInfo;
|
||||
ComponentName componentName = new ComponentName(
|
||||
serviceInfo.packageName, serviceInfo.name);
|
||||
enabledComponents.add(componentName);
|
||||
}
|
||||
mAreEnabledServicesUpdated = false;
|
||||
|
||||
final int printerCount = printers.size();
|
||||
for (int i = printerCount - 1; i >= 0; i--) {
|
||||
|
||||
@@ -22,11 +22,13 @@ import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
@@ -51,10 +53,12 @@ import android.print.PrintAttributes.Resolution;
|
||||
import android.print.PrintDocumentInfo;
|
||||
import android.print.PrintJobInfo;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrintServicesLoader;
|
||||
import android.print.PrinterCapabilitiesInfo;
|
||||
import android.print.PrinterId;
|
||||
import android.print.PrinterInfo;
|
||||
import android.printservice.PrintService;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
@@ -94,7 +98,6 @@ import com.android.printspooler.util.ApprovedPrintServices;
|
||||
import com.android.printspooler.util.MediaSizeUtils;
|
||||
import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
|
||||
import com.android.printspooler.util.PageRangeUtils;
|
||||
import com.android.printspooler.util.PrintOptionUtils;
|
||||
import com.android.printspooler.widget.PrintContentView;
|
||||
import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
|
||||
import com.android.printspooler.widget.PrintContentView.OptionsStateController;
|
||||
@@ -113,12 +116,14 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
|
||||
PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks,
|
||||
OptionsStateChangeListener, OptionsStateController {
|
||||
OptionsStateChangeListener, OptionsStateController,
|
||||
LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
|
||||
private static final String LOG_TAG = "PrintActivity";
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
@@ -129,6 +134,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
|
||||
private static final String HAS_PRINTED_PREF = "has_printed";
|
||||
|
||||
private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 1;
|
||||
private static final int LOADER_ID_PRINT_REGISTRY = 2;
|
||||
private static final int LOADER_ID_PRINT_REGISTRY_INT = 3;
|
||||
|
||||
private static final int ORIENTATION_PORTRAIT = 0;
|
||||
private static final int ORIENTATION_LANDSCAPE = 1;
|
||||
|
||||
@@ -139,7 +148,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
|
||||
|
||||
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_MORE = Integer.MAX_VALUE - 1;
|
||||
|
||||
private static final int STATE_INITIALIZING = 0;
|
||||
private static final int STATE_CONFIGURING = 1;
|
||||
@@ -239,6 +248,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
/** Observer for changes to the printers */
|
||||
private PrintersObserver mPrintersObserver;
|
||||
|
||||
/** Advances options activity name for current printer */
|
||||
private ComponentName mAdvancedPrintOptionsActivity;
|
||||
|
||||
/** Whether at least one print services is enabled or not */
|
||||
private boolean mArePrintServicesEnabled;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -278,6 +293,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getLoaderManager().initLoader(LOADER_ID_ENABLED_PRINT_SERVICES, null, this);
|
||||
}
|
||||
|
||||
private void onConnectedToPrintSpooler(final IBinder documentAdapter) {
|
||||
@@ -292,7 +309,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
public void run() {
|
||||
onPrinterRegistryReady(documentAdapter);
|
||||
}
|
||||
});
|
||||
}, LOADER_ID_PRINT_REGISTRY, LOADER_ID_PRINT_REGISTRY_INT);
|
||||
}
|
||||
|
||||
private void onPrinterRegistryReady(IBinder documentAdapter) {
|
||||
@@ -716,15 +733,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
}
|
||||
|
||||
private void startAdvancedPrintOptionsActivity(PrinterInfo printer) {
|
||||
ComponentName serviceName = printer.getId().getServiceName();
|
||||
|
||||
String activityName = PrintOptionUtils.getAdvancedOptionsActivityName(this, serviceName);
|
||||
if (TextUtils.isEmpty(activityName)) {
|
||||
if (mAdvancedPrintOptionsActivity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(new ComponentName(serviceName.getPackageName(), activityName));
|
||||
intent.setComponent(mAdvancedPrintOptionsActivity);
|
||||
|
||||
List<ResolveInfo> resolvedActivities = getPackageManager()
|
||||
.queryIntentActivities(intent, 0);
|
||||
@@ -1283,6 +1297,59 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
|
||||
return new PrintServicesLoader((PrintManager) getSystemService(Context.PRINT_SERVICE), this,
|
||||
PrintManager.ENABLED_SERVICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
|
||||
List<PrintServiceInfo> services) {
|
||||
ComponentName newAdvancedPrintOptionsActivity = null;
|
||||
if (mCurrentPrinter != null && services != null) {
|
||||
final int numServices = services.size();
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
PrintServiceInfo service = services.get(i);
|
||||
|
||||
if (service.getComponentName().equals(mCurrentPrinter.getId().getServiceName())) {
|
||||
String advancedOptionsActivityName = service.getAdvancedOptionsActivityName();
|
||||
|
||||
if (!TextUtils.isEmpty(advancedOptionsActivityName)) {
|
||||
newAdvancedPrintOptionsActivity = new ComponentName(
|
||||
service.getComponentName().getPackageName(),
|
||||
advancedOptionsActivityName);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Objects.equals(newAdvancedPrintOptionsActivity, mAdvancedPrintOptionsActivity)) {
|
||||
mAdvancedPrintOptionsActivity = newAdvancedPrintOptionsActivity;
|
||||
updateOptionsUi();
|
||||
}
|
||||
|
||||
boolean newArePrintServicesEnabled = services != null && !services.isEmpty();
|
||||
if (mArePrintServicesEnabled != newArePrintServicesEnabled) {
|
||||
mArePrintServicesEnabled = newArePrintServicesEnabled;
|
||||
|
||||
// Reload mDestinationSpinnerAdapter as mArePrintServicesEnabled changed and the adapter
|
||||
// reads that in DestinationAdapter#getMoreItemTitle
|
||||
if (mDestinationSpinnerAdapter != null) {
|
||||
mDestinationSpinnerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
|
||||
if (!isFinishing()) {
|
||||
onLoadFinished(loader, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
|
||||
* dismissed if the same {@link PrintService} gets approved by another
|
||||
@@ -1722,9 +1789,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
}
|
||||
|
||||
// Advanced print options
|
||||
ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
|
||||
if (!TextUtils.isEmpty(PrintOptionUtils.getAdvancedOptionsActivityName(
|
||||
this, serviceName))) {
|
||||
if (mAdvancedPrintOptionsActivity != null) {
|
||||
mMoreOptionsButton.setVisibility(View.VISIBLE);
|
||||
mMoreOptionsButton.setEnabled(true);
|
||||
} else {
|
||||
@@ -2216,14 +2281,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
if (position == 0) {
|
||||
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
|
||||
} else if (position == 1) {
|
||||
return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
|
||||
return DEST_ADAPTER_ITEM_ID_MORE;
|
||||
}
|
||||
} else {
|
||||
if (position == 1) {
|
||||
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
|
||||
}
|
||||
if (position == getCount() - 1) {
|
||||
return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
|
||||
return DEST_ADAPTER_ITEM_ID_MORE;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
@@ -2236,6 +2301,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
return view;
|
||||
}
|
||||
|
||||
private String getMoreItemTitle() {
|
||||
if (mArePrintServicesEnabled) {
|
||||
return getString(R.string.all_printers);
|
||||
} else {
|
||||
return getString(R.string.print_add_printer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (mShowDestinationPrompt) {
|
||||
@@ -2264,7 +2337,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
title = printerHolder.printer.getName();
|
||||
icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf, null);
|
||||
} else if (position == 1) {
|
||||
title = getString(R.string.all_printers);
|
||||
title = getMoreItemTitle();
|
||||
}
|
||||
} else {
|
||||
if (position == 1 && getPdfPrinter() != null) {
|
||||
@@ -2272,7 +2345,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
title = printerHolder.printer.getName();
|
||||
icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf, null);
|
||||
} else if (position == getCount() - 1) {
|
||||
title = getString(R.string.all_printers);
|
||||
title = getMoreItemTitle();
|
||||
} else {
|
||||
PrinterHolder printerHolder = (PrinterHolder) getItem(position);
|
||||
PrinterInfo printInfo = printerHolder.printer;
|
||||
@@ -2307,7 +2380,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
}
|
||||
iconView.setImageDrawable(icon);
|
||||
} else {
|
||||
iconView.setVisibility(View.GONE);
|
||||
iconView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
@@ -2352,6 +2425,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
|
||||
if (updatedPrinter != null) {
|
||||
printerHolder.printer = updatedPrinter;
|
||||
printerHolder.removed = false;
|
||||
} else {
|
||||
printerHolder.removed = true;
|
||||
}
|
||||
@@ -2497,6 +2571,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
updateDocument(false);
|
||||
}
|
||||
|
||||
// Force a reload of the enabled print services to update mAdvancedPrintOptionsActivity
|
||||
// in onLoadFinished();
|
||||
getLoaderManager().getLoader(LOADER_ID_ENABLED_PRINT_SERVICES).forceLoad();
|
||||
|
||||
updateOptionsUi();
|
||||
updateSummary();
|
||||
}
|
||||
@@ -2522,7 +2600,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
|
||||
if (id == DEST_ADAPTER_ITEM_ID_MORE) {
|
||||
startSelectPrinterActivity();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.util.List;
|
||||
|
||||
public class PrinterRegistry {
|
||||
|
||||
private static final int LOADER_ID_PRINTERS_LOADER = 1;
|
||||
private final int mLoaderId;
|
||||
|
||||
private final Activity mActivity;
|
||||
|
||||
@@ -52,12 +52,17 @@ public class PrinterRegistry {
|
||||
public void onPrintersInvalid();
|
||||
}
|
||||
|
||||
public PrinterRegistry(Activity activity, Runnable readyCallback) {
|
||||
public PrinterRegistry(Activity activity, Runnable readyCallback, int loaderId,
|
||||
int internalLoaderId) {
|
||||
mLoaderId = loaderId;
|
||||
mActivity = activity;
|
||||
mReadyCallback = readyCallback;
|
||||
mHandler = new MyHandler(activity.getMainLooper());
|
||||
activity.getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER,
|
||||
null, mLoaderCallbacks);
|
||||
|
||||
Bundle loaderData = new Bundle(1);
|
||||
loaderData.putInt(null, internalLoaderId);
|
||||
|
||||
activity.getLoaderManager().initLoader(loaderId, loaderData, mLoaderCallbacks);
|
||||
}
|
||||
|
||||
public void setOnPrintersChangeListener(OnPrintersChangeListener listener) {
|
||||
@@ -106,7 +111,7 @@ public class PrinterRegistry {
|
||||
}
|
||||
|
||||
private FusedPrintersProvider getPrinterProvider() {
|
||||
Loader<?> loader = mActivity.getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
|
||||
Loader<?> loader = mActivity.getLoaderManager().getLoader(mLoaderId);
|
||||
return (FusedPrintersProvider) loader;
|
||||
}
|
||||
|
||||
@@ -114,38 +119,34 @@ public class PrinterRegistry {
|
||||
new LoaderCallbacks<List<PrinterInfo>>() {
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
|
||||
if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
|
||||
mPrinters.clear();
|
||||
if (mOnPrintersChangeListener != null) {
|
||||
// Post a message as we are in onLoadFinished and certain operations
|
||||
// are not allowed in this callback, such as fragment transactions.
|
||||
// Clients should not handle this explicitly.
|
||||
mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID,
|
||||
mOnPrintersChangeListener).sendToTarget();
|
||||
}
|
||||
mPrinters.clear();
|
||||
if (mOnPrintersChangeListener != null) {
|
||||
// Post a message as we are in onLoadFinished and certain operations
|
||||
// are not allowed in this callback, such as fragment transactions.
|
||||
// Clients should not handle this explicitly.
|
||||
mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID,
|
||||
mOnPrintersChangeListener).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
// LoaderCallbacks#onLoadFinished
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PrinterInfo>> loader, List<PrinterInfo> printers) {
|
||||
if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
|
||||
mPrinters.clear();
|
||||
mPrinters.addAll(printers);
|
||||
if (mOnPrintersChangeListener != null) {
|
||||
// Post a message as we are in onLoadFinished and certain operations
|
||||
// are not allowed in this callback, such as fragment transactions.
|
||||
// Clients should not handle this explicitly.
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = mOnPrintersChangeListener;
|
||||
args.arg2 = printers;
|
||||
mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, args).sendToTarget();
|
||||
}
|
||||
if (!mReady) {
|
||||
mReady = true;
|
||||
if (mReadyCallback != null) {
|
||||
mReadyCallback.run();
|
||||
}
|
||||
mPrinters.clear();
|
||||
mPrinters.addAll(printers);
|
||||
if (mOnPrintersChangeListener != null) {
|
||||
// Post a message as we are in onLoadFinished and certain operations
|
||||
// are not allowed in this callback, such as fragment transactions.
|
||||
// Clients should not handle this explicitly.
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = mOnPrintersChangeListener;
|
||||
args.arg2 = printers;
|
||||
mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, args).sendToTarget();
|
||||
}
|
||||
if (!mReady) {
|
||||
mReady = true;
|
||||
if (mReadyCallback != null) {
|
||||
mReadyCallback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,10 +154,7 @@ public class PrinterRegistry {
|
||||
// LoaderCallbacks#onCreateLoader
|
||||
@Override
|
||||
public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
|
||||
if (id == LOADER_ID_PRINTERS_LOADER) {
|
||||
return new FusedPrintersProvider(mActivity);
|
||||
}
|
||||
return null;
|
||||
return new FusedPrintersProvider(mActivity, args.getInt(null));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,31 +17,18 @@
|
||||
package com.android.printspooler.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.Loader;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrintServicesLoader;
|
||||
import android.print.PrinterId;
|
||||
import android.print.PrinterInfo;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
@@ -59,17 +46,16 @@ import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.printspooler.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -78,35 +64,33 @@ import java.util.List;
|
||||
/**
|
||||
* This is an activity for selecting a printer.
|
||||
*/
|
||||
public final class SelectPrinterActivity extends Activity {
|
||||
public final class SelectPrinterActivity extends Activity implements
|
||||
LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
|
||||
|
||||
private static final String LOG_TAG = "SelectPrinterFragment";
|
||||
|
||||
private static final int LOADER_ID_PRINT_REGISTRY = 1;
|
||||
private static final int LOADER_ID_PRINT_REGISTRY_INT = 2;
|
||||
private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 3;
|
||||
|
||||
public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
|
||||
|
||||
private static final String FRAGMENT_TAG_ADD_PRINTER_DIALOG =
|
||||
"FRAGMENT_TAG_ADD_PRINTER_DIALOG";
|
||||
|
||||
private static final String FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS =
|
||||
"FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS";
|
||||
|
||||
private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
|
||||
|
||||
private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
|
||||
|
||||
/** If there are any enabled print services */
|
||||
private boolean mHasEnabledPrintServices;
|
||||
|
||||
private final ArrayList<PrintServiceInfo> mAddPrinterServices =
|
||||
new ArrayList<>();
|
||||
|
||||
private PrinterRegistry mPrinterRegistry;
|
||||
|
||||
private ListView mListView;
|
||||
|
||||
private AnnounceFilterResult mAnnounceFilterResult;
|
||||
|
||||
/** Monitor if new print services get enabled or disabled */
|
||||
private ContentObserver mPrintServicesDisabledObserver;
|
||||
private PackageMonitor mPackageObserver;
|
||||
private void startAddPrinterActivity() {
|
||||
startActivity(new Intent(this, AddPrinterActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -115,7 +99,8 @@ public final class SelectPrinterActivity extends Activity {
|
||||
|
||||
setContentView(R.layout.select_printer_activity);
|
||||
|
||||
mPrinterRegistry = new PrinterRegistry(this, null);
|
||||
mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
|
||||
LOADER_ID_PRINT_REGISTRY_INT);
|
||||
|
||||
// Hook up the list view.
|
||||
mListView = (ListView) findViewById(android.R.id.list);
|
||||
@@ -145,21 +130,66 @@ public final class SelectPrinterActivity extends Activity {
|
||||
}
|
||||
|
||||
PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
|
||||
onPrinterSelected(printer.getId());
|
||||
|
||||
if (printer == null) {
|
||||
startAddPrinterActivity();
|
||||
} else {
|
||||
onPrinterSelected(printer.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.button).setOnClickListener(new OnClickListener() {
|
||||
@Override public void onClick(View v) {
|
||||
startAddPrinterActivity();
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(mListView);
|
||||
|
||||
// Display a notification about disabled services if there are disabled services
|
||||
String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
|
||||
Settings.Secure.DISABLED_PRINT_SERVICES);
|
||||
if (!TextUtils.isEmpty(disabledServicesSetting)) {
|
||||
Toast.makeText(this, getString(R.string.print_services_disabled_toast),
|
||||
Toast.LENGTH_LONG).show();
|
||||
getLoaderManager().initLoader(LOADER_ID_ENABLED_PRINT_SERVICES, null, this);
|
||||
|
||||
// On first creation:
|
||||
//
|
||||
// If no services are installed, instantly open add printer dialog.
|
||||
// If some are disabled and some are enabled show a toast to notify the user
|
||||
if (savedInstanceState == null || !savedInstanceState.getBoolean(KEY_NOT_FIRST_CREATE)) {
|
||||
List<PrintServiceInfo> allServices =
|
||||
((PrintManager) getSystemService(Context.PRINT_SERVICE))
|
||||
.getPrintServices(PrintManager.ALL_SERVICES);
|
||||
boolean hasEnabledServices = false;
|
||||
boolean hasDisabledServices = false;
|
||||
|
||||
if (allServices != null) {
|
||||
final int numServices = allServices.size();
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
if (allServices.get(i).isEnabled()) {
|
||||
hasEnabledServices = true;
|
||||
} else {
|
||||
hasDisabledServices = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasEnabledServices) {
|
||||
startAddPrinterActivity();
|
||||
} else if (hasDisabledServices) {
|
||||
String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
|
||||
Settings.Secure.DISABLED_PRINT_SERVICES);
|
||||
if (!TextUtils.isEmpty(disabledServicesSetting)) {
|
||||
Toast.makeText(this, getString(R.string.print_services_disabled_toast),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(KEY_NOT_FIRST_CREATE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
@@ -249,60 +279,13 @@ public final class SelectPrinterActivity extends Activity {
|
||||
* Adjust the UI if the enabled print services changed.
|
||||
*/
|
||||
private synchronized void onPrintServicesUpdate() {
|
||||
updateServicesWithAddPrinterActivity();
|
||||
updateEmptyView((DestinationAdapter)mListView.getAdapter());
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register listener for changes to the enabled print services.
|
||||
*/
|
||||
private void registerServiceMonitor() {
|
||||
// Listen for services getting disabled
|
||||
mPrintServicesDisabledObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onPrintServicesUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for services getting installed or uninstalled
|
||||
mPackageObserver = new PackageMonitor() {
|
||||
@Override
|
||||
public void onPackageModified(String packageName) {
|
||||
onPrintServicesUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, int uid) {
|
||||
onPrintServicesUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageAdded(String packageName, int uid) {
|
||||
onPrintServicesUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false,
|
||||
mPrintServicesDisabledObserver);
|
||||
|
||||
mPackageObserver.register(this, getMainLooper(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the listeners for changes to the enabled print services.
|
||||
*/
|
||||
private void unregisterServiceMonitorIfNeeded() {
|
||||
getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver);
|
||||
mPackageObserver.unregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
registerServiceMonitor();
|
||||
onPrintServicesUpdate();
|
||||
}
|
||||
|
||||
@@ -316,19 +299,9 @@ public final class SelectPrinterActivity extends Activity {
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
unregisterServiceMonitorIfNeeded();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_add_printer) {
|
||||
showAddPrinterSelectionDialog();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void onPrinterSelected(PrinterId printerId) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(INTENT_EXTRA_PRINTER_ID, printerId);
|
||||
@@ -336,68 +309,6 @@ public final class SelectPrinterActivity extends Activity {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void updateServicesWithAddPrinterActivity() {
|
||||
mHasEnabledPrintServices = true;
|
||||
mAddPrinterServices.clear();
|
||||
|
||||
// Get all enabled print services.
|
||||
PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
|
||||
List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
|
||||
|
||||
// No enabled print services - done.
|
||||
if (enabledServices.isEmpty()) {
|
||||
mHasEnabledPrintServices = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the services with valid add printers activities.
|
||||
final int enabledServiceCount = enabledServices.size();
|
||||
for (int i = 0; i < enabledServiceCount; i++) {
|
||||
PrintServiceInfo enabledService = enabledServices.get(i);
|
||||
|
||||
// No add printers activity declared - next.
|
||||
if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
|
||||
ComponentName addPrintersComponentName = new ComponentName(
|
||||
serviceInfo.packageName, enabledService.getAddPrintersActivityName());
|
||||
Intent addPritnersIntent = new Intent()
|
||||
.setComponent(addPrintersComponentName);
|
||||
|
||||
// The add printers activity is valid - add it.
|
||||
PackageManager pm = getPackageManager();
|
||||
List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
|
||||
if (!resolvedActivities.isEmpty()) {
|
||||
// The activity is a component name, therefore it is one or none.
|
||||
ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
|
||||
if (activityInfo.exported
|
||||
&& (activityInfo.permission == null
|
||||
|| pm.checkPermission(activityInfo.permission, getPackageName())
|
||||
== PackageManager.PERMISSION_GRANTED)) {
|
||||
mAddPrinterServices.add(enabledService);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showAddPrinterSelectionDialog() {
|
||||
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||
Fragment oldFragment = getFragmentManager().findFragmentByTag(
|
||||
FRAGMENT_TAG_ADD_PRINTER_DIALOG);
|
||||
if (oldFragment != null) {
|
||||
transaction.remove(oldFragment);
|
||||
}
|
||||
AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS,
|
||||
mAddPrinterServices);
|
||||
newFragment.setArguments(arguments);
|
||||
transaction.add(newFragment, FRAGMENT_TAG_ADD_PRINTER_DIALOG);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
public void updateEmptyView(DestinationAdapter adapter) {
|
||||
if (mListView.getEmptyView() == null) {
|
||||
View emptyView = findViewById(R.id.empty_print_state);
|
||||
@@ -426,71 +337,28 @@ public final class SelectPrinterActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
public static class AddPrinterAlertDialogFragment extends DialogFragment {
|
||||
@Override
|
||||
public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
|
||||
return new PrintServicesLoader((PrintManager) getSystemService(Context.PRINT_SERVICE), this,
|
||||
PrintManager.ENABLED_SERVICES);
|
||||
}
|
||||
|
||||
private String mAddPrintServiceItem;
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
|
||||
List<PrintServiceInfo> data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
mHasEnabledPrintServices = false;
|
||||
} else {
|
||||
mHasEnabledPrintServices = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.choose_print_service);
|
||||
onPrintServicesUpdate();
|
||||
}
|
||||
|
||||
final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
|
||||
getArguments().getParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS);
|
||||
|
||||
final ArrayAdapter<String> adapter = new ArrayAdapter<>(
|
||||
getActivity(), android.R.layout.simple_list_item_1);
|
||||
final int printServiceCount = printServices.size();
|
||||
for (int i = 0; i < printServiceCount; i++) {
|
||||
PrintServiceInfo printService = printServices.get(i);
|
||||
adapter.add(printService.getResolveInfo().loadLabel(
|
||||
getActivity().getPackageManager()).toString());
|
||||
}
|
||||
|
||||
final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
|
||||
Settings.Secure.PRINT_SERVICE_SEARCH_URI);
|
||||
final Intent viewIntent;
|
||||
if (!TextUtils.isEmpty(searchUri)) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
|
||||
if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
|
||||
viewIntent = intent;
|
||||
mAddPrintServiceItem = getString(R.string.add_print_service_label);
|
||||
adapter.add(mAddPrintServiceItem);
|
||||
} else {
|
||||
viewIntent = null;
|
||||
}
|
||||
} else {
|
||||
viewIntent = null;
|
||||
}
|
||||
|
||||
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String item = adapter.getItem(which);
|
||||
if (item.equals(mAddPrintServiceItem)) {
|
||||
try {
|
||||
startActivity(viewIntent);
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
|
||||
}
|
||||
} else {
|
||||
PrintServiceInfo printService = printServices.get(which);
|
||||
ComponentName componentName = new ComponentName(
|
||||
printService.getResolveInfo().serviceInfo.packageName,
|
||||
printService.getAddPrintersActivityName());
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(componentName);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return builder.create();
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
|
||||
if (!isFinishing()) {
|
||||
onLoadFinished(loader, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,14 +460,40 @@ public final class SelectPrinterActivity extends Activity {
|
||||
@Override
|
||||
public int getCount() {
|
||||
synchronized (mLock) {
|
||||
return mFilteredPrinters.size();
|
||||
if (mFilteredPrinters.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
// Add "add printer" item to the end of the list. If the list is empty there is
|
||||
// a link on the empty view
|
||||
return mFilteredPrinters.size() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
// Use separate view types for the "add printer" item an the items referring to printers
|
||||
if (getItem(position) == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
synchronized (mLock) {
|
||||
return mFilteredPrinters.get(position);
|
||||
if (position < mFilteredPrinters.size()) {
|
||||
return mFilteredPrinters.get(position);
|
||||
} else {
|
||||
// Return null to mark this as the "add printer item"
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,6 +509,18 @@ public final class SelectPrinterActivity extends Activity {
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final PrinterInfo printer = (PrinterInfo) getItem(position);
|
||||
|
||||
// Handle "add printer item"
|
||||
if (printer == null) {
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.add_printer_list_item,
|
||||
parent, false);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(
|
||||
R.layout.printer_list_item, parent, false);
|
||||
@@ -622,7 +528,6 @@ public final class SelectPrinterActivity extends Activity {
|
||||
|
||||
convertView.setEnabled(isActionable(position));
|
||||
|
||||
final PrinterInfo printer = (PrinterInfo) getItem(position);
|
||||
|
||||
CharSequence title = printer.getName();
|
||||
Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
|
||||
@@ -661,7 +566,7 @@ public final class SelectPrinterActivity extends Activity {
|
||||
subtitleView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
ImageView moreInfoView = (ImageView) convertView.findViewById(R.id.more_info);
|
||||
LinearLayout moreInfoView = (LinearLayout) convertView.findViewById(R.id.more_info);
|
||||
if (printer.getInfoIntent() != null) {
|
||||
moreInfoView.setVisibility(View.VISIBLE);
|
||||
moreInfoView.setOnClickListener(new OnClickListener() {
|
||||
@@ -699,7 +604,12 @@ public final class SelectPrinterActivity extends Activity {
|
||||
|
||||
public boolean isActionable(int position) {
|
||||
PrinterInfo printer = (PrinterInfo) getItem(position);
|
||||
return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
|
||||
|
||||
if (printer == null) {
|
||||
return true;
|
||||
} else {
|
||||
return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.printspooler.util;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.print.PrintManager;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PrintOptionUtils {
|
||||
|
||||
private PrintOptionUtils() {
|
||||
/* ignore - hide constructor */
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the advanced options activity name for a print service.
|
||||
*
|
||||
* @param context Context for accessing system resources.
|
||||
* @param serviceName The print service name.
|
||||
* @return The advanced options activity name or null.
|
||||
*/
|
||||
public static String getAdvancedOptionsActivityName(Context context,
|
||||
ComponentName serviceName) {
|
||||
PrintManager printManager = (PrintManager) context.getSystemService(
|
||||
Context.PRINT_SERVICE);
|
||||
List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
|
||||
final int printServiceCount = printServices.size();
|
||||
for (int i = 0; i < printServiceCount; i ++) {
|
||||
PrintServiceInfo printServiceInfo = printServices.get(i);
|
||||
ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
|
||||
if (serviceInfo.name.equals(serviceName.getClassName())
|
||||
&& serviceInfo.packageName.equals(serviceName.getPackageName())) {
|
||||
return printServiceInfo.getAdvancedOptionsActivityName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.print.PrintManager;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.MediaStore;
|
||||
@@ -579,7 +580,7 @@ final class DefaultPermissionGrantPolicy {
|
||||
|
||||
// Print Spooler
|
||||
PackageParser.Package printSpoolerPackage = getSystemPackageLPr(
|
||||
"com.android.printspooler");
|
||||
PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
|
||||
if (printSpoolerPackage != null
|
||||
&& doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
|
||||
grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
|
||||
|
||||
@@ -41,13 +41,16 @@ import android.os.UserManager;
|
||||
import android.print.IPrintDocumentAdapter;
|
||||
import android.print.IPrintJobStateChangeListener;
|
||||
import android.print.IPrintManager;
|
||||
import android.print.IPrintServicesChangeListener;
|
||||
import android.print.IPrinterDiscoveryObserver;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintJobId;
|
||||
import android.print.PrintJobInfo;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrinterId;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
@@ -66,6 +69,8 @@ import java.util.List;
|
||||
* PrintManager implementation is contained within.
|
||||
*/
|
||||
public final class PrintManagerService extends SystemService {
|
||||
private static final String LOG_TAG = "PrintManagerService";
|
||||
|
||||
private final PrintManagerImpl mPrintManagerImpl;
|
||||
|
||||
public PrintManagerService(Context context) {
|
||||
@@ -253,7 +258,10 @@ public final class PrintManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
|
||||
public List<PrintServiceInfo> getPrintServices(int selectionFlags, int userId) {
|
||||
Preconditions.checkFlagsArgument(selectionFlags,
|
||||
PrintManager.DISABLED_SERVICES | PrintManager.ENABLED_SERVICES);
|
||||
|
||||
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
|
||||
final UserState userState;
|
||||
synchronized (mLock) {
|
||||
@@ -262,34 +270,44 @@ public final class PrintManagerService extends SystemService {
|
||||
return null;
|
||||
}
|
||||
userState = getOrCreateUserStateLocked(resolvedUserId);
|
||||
|
||||
// The user state might be updated via the same observer-set as the caller of this
|
||||
// interface. If the caller is called back first the user state is not yet updated
|
||||
// and the user gets and inconsistent view. Hence force an update.
|
||||
userState.updateIfNeededLocked();
|
||||
}
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return userState.getEnabledPrintServices();
|
||||
return userState.getPrintServices(selectionFlags);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
|
||||
public void setPrintServiceEnabled(ComponentName service, boolean isEnabled, int userId) {
|
||||
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
|
||||
final int appId = UserHandle.getAppId(Binder.getCallingUid());
|
||||
|
||||
try {
|
||||
if (appId != Process.SYSTEM_UID && appId != UserHandle.getAppId(
|
||||
mContext.getPackageManager().getPackageUidAsUser(
|
||||
PrintManager.PRINT_SPOOLER_PACKAGE_NAME, resolvedUserId))) {
|
||||
throw new SecurityException("Only system and print spooler can call this");
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Could not verify caller", e);
|
||||
return;
|
||||
}
|
||||
|
||||
service = Preconditions.checkNotNull(service);
|
||||
|
||||
final UserState userState;
|
||||
synchronized (mLock) {
|
||||
// Only the current group members can get installed services.
|
||||
// Only the current group members can enable / disable services.
|
||||
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
userState = getOrCreateUserStateLocked(resolvedUserId);
|
||||
}
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return userState.getInstalledPrintServices();
|
||||
userState.setPrintServiceEnabled(service, isEnabled);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
@@ -495,6 +513,50 @@ public final class PrintManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPrintServicesChangeListener(IPrintServicesChangeListener listener,
|
||||
int userId) throws RemoteException {
|
||||
listener = Preconditions.checkNotNull(listener);
|
||||
|
||||
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
|
||||
final UserState userState;
|
||||
synchronized (mLock) {
|
||||
// Only the current group members can add a print services listener.
|
||||
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
|
||||
return;
|
||||
}
|
||||
userState = getOrCreateUserStateLocked(resolvedUserId);
|
||||
}
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
userState.addPrintServicesChangeListener(listener);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePrintServicesChangeListener(IPrintServicesChangeListener listener,
|
||||
int userId) {
|
||||
listener = Preconditions.checkNotNull(listener);
|
||||
|
||||
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
|
||||
final UserState userState;
|
||||
synchronized (mLock) {
|
||||
// Only the current group members can remove a print job listener.
|
||||
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
|
||||
return;
|
||||
}
|
||||
userState = getOrCreateUserStateLocked(resolvedUserId);
|
||||
}
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
userState.removePrintServicesChangeListener(listener);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
fd = Preconditions.checkNotNull(fd);
|
||||
@@ -560,7 +622,7 @@ public final class PrintManagerService extends SystemService {
|
||||
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
|
||||
|
||||
List<PrintServiceInfo> installedServices = userState
|
||||
.getInstalledPrintServices();
|
||||
.getPrintServices(PrintManager.ALL_SERVICES);
|
||||
final int numInstalledServices = installedServices.size();
|
||||
for (int i = 0; i < numInstalledServices; i++) {
|
||||
if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
|
||||
@@ -601,7 +663,7 @@ public final class PrintManagerService extends SystemService {
|
||||
boolean stoppedSomePackages = false;
|
||||
|
||||
List<PrintServiceInfo> enabledServices = userState
|
||||
.getEnabledPrintServices();
|
||||
.getPrintServices(PrintManager.ENABLED_SERVICES);
|
||||
if (enabledServices == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.print.IPrintSpoolerCallbacks;
|
||||
import android.print.IPrintSpoolerClient;
|
||||
import android.print.PrintJobId;
|
||||
import android.print.PrintJobInfo;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrinterId;
|
||||
import android.printservice.PrintService;
|
||||
import android.util.Slog;
|
||||
@@ -115,8 +116,8 @@ final class RemotePrintSpooler {
|
||||
mCallbacks = callbacks;
|
||||
mClient = new PrintSpoolerClient(this);
|
||||
mIntent = new Intent();
|
||||
mIntent.setComponent(new ComponentName("com.android.printspooler",
|
||||
"com.android.printspooler.model.PrintSpoolerService"));
|
||||
mIntent.setComponent(new ComponentName(PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
|
||||
PrintManager.PRINT_SPOOLER_PACKAGE_NAME + ".model.PrintSpoolerService"));
|
||||
}
|
||||
|
||||
public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.print.IPrintDocumentAdapter;
|
||||
import android.print.IPrintJobStateChangeListener;
|
||||
import android.print.IPrintServicesChangeListener;
|
||||
import android.print.IPrinterDiscoveryObserver;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintJobId;
|
||||
@@ -121,6 +122,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
|
||||
private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
|
||||
|
||||
private List<PrintServicesChangeListenerRecord> mPrintServicesChangeListenerRecords;
|
||||
|
||||
private boolean mDestroyed;
|
||||
|
||||
public UserState(Context context, int userId, Object lock) {
|
||||
@@ -342,29 +345,63 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
|
||||
}
|
||||
|
||||
public @Nullable List<PrintServiceInfo> getEnabledPrintServices() {
|
||||
public @Nullable List<PrintServiceInfo> getPrintServices(int selectionFlags) {
|
||||
synchronized (mLock) {
|
||||
List<PrintServiceInfo> enabledServices = null;
|
||||
List<PrintServiceInfo> selectedServices = null;
|
||||
final int installedServiceCount = mInstalledServices.size();
|
||||
for (int i = 0; i < installedServiceCount; i++) {
|
||||
PrintServiceInfo installedService = mInstalledServices.get(i);
|
||||
|
||||
ComponentName componentName = new ComponentName(
|
||||
installedService.getResolveInfo().serviceInfo.packageName,
|
||||
installedService.getResolveInfo().serviceInfo.name);
|
||||
if (mActiveServices.containsKey(componentName)) {
|
||||
if (enabledServices == null) {
|
||||
enabledServices = new ArrayList<PrintServiceInfo>();
|
||||
|
||||
// Update isEnabled under the same lock the final returned list is created
|
||||
installedService.setIsEnabled(mActiveServices.containsKey(componentName));
|
||||
|
||||
if (installedService.isEnabled()) {
|
||||
if ((selectionFlags & PrintManager.ENABLED_SERVICES) == 0) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ((selectionFlags & PrintManager.DISABLED_SERVICES) == 0) {
|
||||
continue;
|
||||
}
|
||||
enabledServices.add(installedService);
|
||||
}
|
||||
|
||||
if (selectedServices == null) {
|
||||
selectedServices = new ArrayList<>();
|
||||
}
|
||||
selectedServices.add(installedService);
|
||||
}
|
||||
return enabledServices;
|
||||
return selectedServices;
|
||||
}
|
||||
}
|
||||
|
||||
public List<PrintServiceInfo> getInstalledPrintServices() {
|
||||
public void setPrintServiceEnabled(@NonNull ComponentName serviceName, boolean isEnabled) {
|
||||
synchronized (mLock) {
|
||||
return mInstalledServices;
|
||||
boolean isChanged = false;
|
||||
if (isEnabled) {
|
||||
isChanged = mDisabledServices.remove(serviceName);
|
||||
} else {
|
||||
// Make sure to only disable services that are currently installed
|
||||
final int numServices = mInstalledServices.size();
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
PrintServiceInfo service = mInstalledServices.get(i);
|
||||
|
||||
if (service.getComponentName().equals(serviceName)) {
|
||||
mDisabledServices.add(serviceName);
|
||||
isChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isChanged) {
|
||||
writeDisabledPrintServicesLocked(mDisabledServices);
|
||||
|
||||
onConfigurationChangedLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,6 +560,44 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
}
|
||||
}
|
||||
|
||||
public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener)
|
||||
throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
if (mPrintServicesChangeListenerRecords == null) {
|
||||
mPrintServicesChangeListenerRecords = new ArrayList<>();
|
||||
}
|
||||
mPrintServicesChangeListenerRecords.add(
|
||||
new PrintServicesChangeListenerRecord(listener) {
|
||||
@Override
|
||||
public void onBinderDied() {
|
||||
mPrintServicesChangeListenerRecords.remove(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) {
|
||||
synchronized (mLock) {
|
||||
throwIfDestroyedLocked();
|
||||
if (mPrintServicesChangeListenerRecords == null) {
|
||||
return;
|
||||
}
|
||||
final int recordCount = mPrintServicesChangeListenerRecords.size();
|
||||
for (int i = 0; i < recordCount; i++) {
|
||||
PrintServicesChangeListenerRecord record =
|
||||
mPrintServicesChangeListenerRecords.get(i);
|
||||
if (record.listener.asBinder().equals(listener.asBinder())) {
|
||||
mPrintServicesChangeListenerRecords.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mPrintServicesChangeListenerRecords.isEmpty()) {
|
||||
mPrintServicesChangeListenerRecords = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintJobStateChanged(PrintJobInfo printJob) {
|
||||
mPrintJobForAppCache.onPrintJobStateChanged(printJob);
|
||||
@@ -530,6 +605,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
printJob.getAppId(), 0, printJob.getId()).sendToTarget();
|
||||
}
|
||||
|
||||
public void onPrintServicesChanged() {
|
||||
mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintersAdded(List<PrinterInfo> printers) {
|
||||
synchronized (mLock) {
|
||||
@@ -894,6 +973,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
onPrintServicesChanged();
|
||||
}
|
||||
|
||||
private void addServiceLocked(RemotePrintService service) {
|
||||
@@ -978,8 +1059,29 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDispatchPrintServicesChanged() {
|
||||
final List<PrintServicesChangeListenerRecord> records;
|
||||
synchronized (mLock) {
|
||||
if (mPrintServicesChangeListenerRecords == null) {
|
||||
return;
|
||||
}
|
||||
records = new ArrayList<>(mPrintServicesChangeListenerRecords);
|
||||
}
|
||||
final int recordCount = records.size();
|
||||
for (int i = 0; i < recordCount; i++) {
|
||||
PrintServicesChangeListenerRecord record = records.get(i);
|
||||
|
||||
try {
|
||||
record.listener.onPrintServicesChanged();;
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error notifying for print services change", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class UserStateHandler extends Handler {
|
||||
public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
|
||||
public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
|
||||
|
||||
public UserStateHandler(Looper looper) {
|
||||
super(looper, null, false);
|
||||
@@ -987,10 +1089,17 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message.what == MSG_DISPATCH_PRINT_JOB_STATE_CHANGED) {
|
||||
PrintJobId printJobId = (PrintJobId) message.obj;
|
||||
final int appId = message.arg1;
|
||||
handleDispatchPrintJobStateChanged(printJobId, appId);
|
||||
switch (message.what) {
|
||||
case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED:
|
||||
PrintJobId printJobId = (PrintJobId) message.obj;
|
||||
final int appId = message.arg1;
|
||||
handleDispatchPrintJobStateChanged(printJobId, appId);
|
||||
break;
|
||||
case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
|
||||
handleDispatchPrintServicesChanged();
|
||||
break;
|
||||
default:
|
||||
// not reached
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1015,6 +1124,23 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
||||
public abstract void onBinderDied();
|
||||
}
|
||||
|
||||
private abstract class PrintServicesChangeListenerRecord implements DeathRecipient {
|
||||
@NonNull final IPrintServicesChangeListener listener;
|
||||
|
||||
public PrintServicesChangeListenerRecord(@NonNull IPrintServicesChangeListener listener) throws RemoteException {
|
||||
this.listener = listener;
|
||||
listener.asBinder().linkToDeath(this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
listener.asBinder().unlinkToDeath(this, 0);
|
||||
onBinderDied();
|
||||
}
|
||||
|
||||
public abstract void onBinderDied();
|
||||
}
|
||||
|
||||
private class PrinterDiscoverySessionMediator {
|
||||
private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
|
||||
new ArrayMap<PrinterId, PrinterInfo>();
|
||||
|
||||
Reference in New Issue
Block a user