diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index fb6bb2e9ec906..d2ae5e6f92f2a 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -22,6 +22,7 @@ import android.print.IPrintClient; import android.print.PrinterId; import android.print.PrintJobInfo; import android.print.PrintAttributes; +import android.printservice.PrintServiceInfo; /** * Interface for communication with the core print manager service. @@ -37,6 +38,8 @@ interface IPrintManager { void cancelPrintJob(int printJobId, int appId, int userId); void restartPrintJob(int printJobId, int appId, int userId); + List getEnabledPrintServices(int userId); + void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); void startPrinterDiscovery(in IPrinterDiscoveryObserver observer, in List priorityList, int userId); diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 6e32c05fc24cd..10cc7715c5994 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -28,6 +28,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.print.PrintDocumentAdapter.LayoutResultCallback; import android.print.PrintDocumentAdapter.WriteResultCallback; +import android.printservice.PrintServiceInfo; import android.text.TextUtils; import android.util.Log; @@ -203,6 +204,25 @@ public final class PrintManager { return null; } + /** + * Gets the list of enabled print services. + * + * @return The enabled service list or an empty list. + * + * @hide + */ + public List getEnabledPrintServices() { + try { + List enabledServices = mService.getEnabledPrintServices(mUserId); + if (enabledServices != null) { + return enabledServices; + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error getting the enalbed print services", re); + } + return Collections.emptyList(); + } + /** * @hide */ diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 012e76ab9dd18..e5ebf7754cf63 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -178,6 +178,14 @@ public abstract class PrintService extends Service { * For detailed configuration options that can be specified via the meta-data * refer to {@link android.R.styleable#PrintService android.R.styleable.PrintService}. *

+ *

+ * If you declare a settings or add a printers activity, they have to be exported, + * by setting the {@link android.R.attr#exported} activity attribute to true + * . Also in case you want only the system to be able to start any of these + * activities you can specify that they request the android.permission + * .START_PRINT_SERVICE_CONFIG_ACTIVITY permission by setting the + * {@link android.R.attr#permission} activity attribute. + *

*/ public static final String SERVICE_META_DATA = "android.printservice"; diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml index 83ec1adf33fee..9319025df92c1 100644 --- a/packages/PrintSpooler/AndroidManifest.xml +++ b/packages/PrintSpooler/AndroidManifest.xml @@ -22,17 +22,24 @@ android:versionCode="1"> + on the device. Usually an app can access only the print jobs it created. --> + + + - + diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index 235a7a1b44ece..21a48673eb753 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -145,4 +145,12 @@ Allows the holder to access print jobs created by another app. Should never be needed for normal apps. + + start print service configuration activities + + Allows the holder to start the + configuration activities of a print service. Should never be needed for normal apps. + diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 5c3d70037c79f..14f60f177438e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -62,9 +62,11 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.MeasureSpec; +import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; @@ -1371,7 +1373,8 @@ public class PrintJobConfigActivity extends Activity { null, false); // First animation - fade out the old content. - hidingView.animate().alpha(0.0f).withLayer().withEndAction(new Runnable() { + AutoCancellingAnimator.animate(hidingView).alpha(0.0f) + .withLayer().withEndAction(new Runnable() { @Override public void run() { hidingView.setVisibility(View.INVISIBLE); @@ -1390,8 +1393,8 @@ public class PrintJobConfigActivity extends Activity { / (float) contentContainer.getHeight(); // Second animation - resize the container. - contentContainer.animate().scaleY(scaleY).withLayer().withEndAction( - new Runnable() { + AutoCancellingAnimator.animate(contentContainer).scaleY(scaleY).withLayer() + .withEndAction(new Runnable() { @Override public void run() { // Swap the old and the new content. @@ -1400,8 +1403,8 @@ public class PrintJobConfigActivity extends Activity { contentContainer.addView(showingView); // Third animation - show the new content. - showingView.animate().withLayer().alpha(1.0f).withEndAction( - new Runnable() { + AutoCancellingAnimator.animate(showingView).withLayer().alpha(1.0f) + .withEndAction(new Runnable() { @Override public void run() { postAnimateCommand.run(); @@ -2212,4 +2215,67 @@ public class PrintJobConfigActivity extends Activity { } } } + + private static final class AutoCancellingAnimator + implements OnAttachStateChangeListener, Runnable { + + private ViewPropertyAnimator mAnimator; + + private boolean mCancelled; + private Runnable mEndCallback; + + public static AutoCancellingAnimator animate(View view) { + ViewPropertyAnimator animator = view.animate(); + AutoCancellingAnimator cancellingWrapper = + new AutoCancellingAnimator(animator); + view.addOnAttachStateChangeListener(cancellingWrapper); + return cancellingWrapper; + } + + private AutoCancellingAnimator(ViewPropertyAnimator animator) { + mAnimator = animator; + } + + public AutoCancellingAnimator alpha(float alpha) { + mAnimator = mAnimator.alpha(alpha); + return this; + } + + public void cancel() { + mAnimator.cancel(); + } + + public AutoCancellingAnimator withLayer() { + mAnimator = mAnimator.withLayer(); + return this; + } + + public AutoCancellingAnimator withEndAction(Runnable callback) { + mEndCallback = callback; + mAnimator = mAnimator.withEndAction(this); + return this; + } + + public AutoCancellingAnimator scaleY(float scale) { + mAnimator = mAnimator.scaleY(scale); + return this; + } + + @Override + public void onViewAttachedToWindow(View v) { + /* do nothing */ + } + + @Override + public void onViewDetachedFromWindow(View v) { + cancel(); + } + + @Override + public void run() { + if (!mCancelled) { + mEndCallback.run(); + } + } + } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java index 9ca3a869626a8..c397c40655308 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java +++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java @@ -24,20 +24,26 @@ import android.app.Fragment; import android.app.FragmentTransaction; import android.app.ListFragment; 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.pm.ActivityInfo; 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.net.Uri; import android.os.Bundle; +import android.print.PrintManager; import android.print.PrinterId; import android.print.PrinterInfo; import android.printservice.PrintServiceInfo; import android.text.TextUtils; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -59,6 +65,8 @@ import java.util.List; */ public final class SelectPrinterFragment extends ListFragment { + private static final String LOG_TAG = "SelectPrinterFragment"; + private static final int LOADER_ID_PRINTERS_LOADER = 1; private static final String FRAGMRNT_TAG_ADD_PRINTER_DIALOG = @@ -142,40 +150,45 @@ public final class SelectPrinterFragment extends ListFragment { private void updateAddPrintersAdapter() { mAddPrinterServices.clear(); - // Get all print services. - List resolveInfos = getActivity().getPackageManager().queryIntentServices( - new Intent(android.printservice.PrintService.SERVICE_INTERFACE), - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + // Get all enabled print services. + PrintManager printManager = (PrintManager) getActivity() + .getSystemService(Context.PRINT_SERVICE); + List enabledServices = printManager.getEnabledPrintServices(); - // No print services - done. - if (resolveInfos.isEmpty()) { + // No enabled print services - done. + if (enabledServices.isEmpty()) { return; } // Find the services with valid add printers activities. - final int resolveInfoCount = resolveInfos.size(); - for (int i = 0; i < resolveInfoCount; i++) { - ResolveInfo resolveInfo = resolveInfos.get(i); - - PrintServiceInfo printServiceInfo = PrintServiceInfo.create( - resolveInfo, getActivity()); - String addPrintersActivity = printServiceInfo.getAddPrintersActivityName(); + final int enabledServiceCount = enabledServices.size(); + for (int i = 0; i < enabledServiceCount; i++) { + PrintServiceInfo enabledService = enabledServices.get(i); // No add printers activity declared - done. - if (TextUtils.isEmpty(addPrintersActivity)) { + if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) { continue; } + ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; ComponentName addPrintersComponentName = new ComponentName( - resolveInfo.serviceInfo.packageName, - addPrintersActivity); - Intent addPritnersIntent = new Intent(Intent.ACTION_MAIN) + serviceInfo.packageName, enabledService.getAddPrintersActivityName()); + Intent addPritnersIntent = new Intent() .setComponent(addPrintersComponentName); // The add printers activity is valid - add it. - if (!getActivity().getPackageManager().queryIntentActivities( - addPritnersIntent, 0).isEmpty()) { - mAddPrinterServices.add(printServiceInfo); + PackageManager pm = getActivity().getPackageManager(); + List 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, + getActivity().getPackageName()) + == PackageManager.PERMISSION_GRANTED)) { + mAddPrinterServices.add(enabledService); + } } } } @@ -228,7 +241,11 @@ public final class SelectPrinterFragment extends ListFragment { printService.getAddPrintersActivityName()); Intent intent = new Intent(Intent.ACTION_MAIN); intent.setComponent(componentName); - startActivity(intent); + try { + startActivity(intent); + } catch (ActivityNotFoundException anfe) { + Log.w(LOG_TAG, "Couldn't start settings activity", anfe); + } } }); @@ -238,7 +255,11 @@ public final class SelectPrinterFragment extends ListFragment { builder.setPositiveButton(R.string.search_play_store, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { - startActivity(marketIntent); + try { + startActivity(marketIntent); + } catch (ActivityNotFoundException anfe) { + Log.w(LOG_TAG, "Couldn't start add printer activity", anfe); + } } }); } diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java index 926f822d0edae..c33bfb78e099e 100644 --- a/services/java/com/android/server/print/PrintManagerService.java +++ b/services/java/com/android/server/print/PrintManagerService.java @@ -35,6 +35,7 @@ import android.print.IPrinterDiscoveryObserver; import android.print.PrintAttributes; import android.print.PrintJobInfo; import android.print.PrinterId; +import android.printservice.PrintServiceInfo; import android.provider.Settings; import android.util.SparseArray; @@ -192,6 +193,22 @@ public final class PrintManagerService extends IPrintManager.Stub { } } + + @Override + public List getEnabledPrintServices(int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + return userState.getEnabledPrintServices(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId) { diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index 86a5aedc3ebc1..6b68c617119f9 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -134,6 +134,26 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } + public List getEnabledPrintServices() { + synchronized (mLock) { + List enabledServices = 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(); + } + enabledServices.add(installedService); + } + } + return enabledServices; + } + } + public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked();