I'm trying to use enableSystemApp method to activate default system apps after provisioning device with the app that is set to device owner mode.
There are two methods to do this:
1) void enableSystemApp (ComponentName admin, String packageName) - in this case you need to pass package name explicitly as String. It works fine, the app gets enabled.
For example, calling this
devicePolicyManager.enableSystemApp(deviceAdminComponent, "com.google.android.gm");
enables default Gmail client, which is disabled after provisioning.
2) int enableSystemApp (ComponentName admin, Intent intent) - in this case, you need to pass an implicit intent and Android should enable all system apps that match this intent. In addition, this method returns int number of apps that match the intent. And here's the problem - I can't get this method to work, it always returns 0 and doesn't enable anything.
Here's the snippet I'm trying to use:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
int i = devicePolicyManager.enableSystemApp(deviceAdminComponent, intent);
It does not work and i == 0 in this case. What am I doing wrong?
Any help is appreciated!
Under the hood, the method that accepts an intent queries to get the list of activities that respond to that intent and then loops through the list passing in the package name string to enable the package. It's similar to doing this:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
List<ResolveInfo> infoes = getPackageManager()
.queryIntentActivities(intent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
for (ResolveInfo info in infoes) {
devicePolicyManager.enableSystemApp(deviceAdminComponent, info.activityInfo.packageName);
}
Since you are able to enable the app using the package name string, the fault most likely lies in the way the intent is being resolved - which is supported by the fact that it always returns 0.
It is counter-intuitive, but my suspicion is that the application does not resolve the ACTION_MAIN intent because the app is disabled. Have you tried a less generic intent? I would try the following
Intent i;
// #1
// This goes full circle, but I expect it should work
i = getPackageManager().getLaunchIntentForPackage("com.google.android.gm")
// #2
i = new Intent(Intent.ACTION_SEND).setPackageName("com.google.android.gm");
// #3
// Generic, but should resolve _all_ email apps - not just the default one.
// The mailto schema filters out non-email apps
i = new Intent(Intent.ACTION_VIEW , Uri.parse("mailto:"));
Option #1 and #2 are more academic. Both require the package name at which point you may as well use the string overload of enableSystemApp. Option #3 is my best guess for something generic that might still work, but it's possible that it still won't work because the app is disabled.
Note: I find it interesting that enableSystemApp only passes the MATCH_DIRECT_BOOT_AWARE and MATCH_DIRECT_BOOT_UNAWARE flags when querying activities that can resolve the intent, because the MATCH_DISABLED_COMPONENTS and MATCH_SYSTEM_ONLY flags seem much more relevant in this situation.
Related
This is the log, I am receving on crash in Samsung running Oero OS:
Fatal Exception: java.lang.SecurityException: Permission Denial:
starting Intent { act=android.intent.action.SEND typ=text/plain
flg=0x80001 pkg=com.google.android.apps.maps
cmp=com.google.android.apps.maps/com.google.android.apps.gmm.sharing.SendTextToClipboardActivity
clip={text/plain T:"XYZ"
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
share.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
share.putExtra(Intent.EXTRA_TEXT, shareMessage);
share.setPackage(packageName);
Please suggest.
Thanks
You have a couple of options here. You can avoid this crash by specifically filtering out the SendTextToClipboardActivity that Google Maps recently added as an intent-handler. (It looks exactly like the genuine Android "Copy to clipboard" share-target, and is most likely the result of a bug on the part of Google Maps.) Or you can filter out any ResolveInfo where the corresponding Activity is not exported; however, this could result in also filtering out share-targets within your own app that you do want to show to the user, in which case you would need to allow non-exported Activities from your own app package.
For example, suppose you have something like this to obtain a list of providers for your ACTION_SEND intent:
PackageManager packageManager = mActivity.getPackageManager();
List<ResolveInfo> providers = packageManager.queryIntentActivities(sendIntent, 0);
You could then filter out providers that are known to cause problems, and/or providers for which the Activity isn't marked as exported. For example, you could use a check such as the following to build your own list of providers to be displayed to the user:
for (ResolveInfo provider : providers) {
if ("com.google.android.apps.maps".equalsIgnoreCase(provider.activityInfo.packageName)
&& "com.google.android.apps.gmm.sharing.SendTextToClipboardActivity".equalsIgnoreCase(provider.activityInfo.name)) {
continue; // Skip specific Activity you don't want to show
}
if (!BuildConfig.APPLICATION_ID.equalsIgnoreCase(provider.activityInfo.packageName) && !provider.activityInfo.exported) {
continue; // Skip providers where the Activity is not marked with exported=true, unless they're from our own app
}
acceptableProviders.add(provider);
}
How to then go about displaying the acceptableProviders as share options is left as an exercise to the reader. The key is filtering out known bad providers.
Meanwhile, you can also contact Google to complain about the bad behavior of this new intent-handler, which is essentially masquerading as the trusted Android text/plain handler used for the "Copy to clipboard" share-target.
I did peel the documentation in order to see if there is any method that can check if a particular Settings exists on the current device but I did not find anything.
For example, I would like to do the following:
if "Settings.ACTION_DATA_ROAMING_SETTINGS" exists on the current device then ...
If anyone has the answer, I'm taker.
Intent intent = new Intent(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS, null);
List<ResolveInfo> activities = getPackageManager().queryIntentActivities(intent, 0);
Run the above code to check whether there are some components that handle this specific action. If the activities is not empty, the answer is yes.
Given an applications package name how to know if that application supports widgets on homescreen ??
David Wasser's solution was my first guess too, but it failed to detect one of my widget.
Here's another approach if the first one doesn't work for you.
You can list all receivers for the ACTION_APP_WIDGET_UPDATE action, which allhome screen widget should listen to, and then filter by package name :
PackageManager pm = getPackageManager();
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
List<ResolveInfo> list = pm.queryBroadcastReceivers(intent, 0);
for (ResolveInfo info : list) {
Log.i("package", info.activityInfo.packageName);
}
Use PackageManager and call getPackageInfo(packageName, GET_RECEIVERS) for the application.
Loop through all PackageInfo.receivers and call PackageManager.getReceiverInfo(componentName, GET_META_DATA) for each ActivityInfo object in PackageInfo.receivers.
Look at the field metadata. This may be null or it may contain a Bundle. If it contains a Bundle, look in the Bundle for an item with key "android.appwidget.provider". If you find that then you can be pretty sure that the application supports widgets on the homescreen.
i'm programming an android app, a Tasker, and i really don't find the way to obtain the label of another app. This is my point, i'm using an ListActivity where you select an installed app, then when you click it creates the Intent and all the app stuff, what i wanna do is to show to the user the android:label from the app he selected, i found in ResolverInfo an attribute called .activityInfo.labelRes, and i think is the label descriptor for the R class of the app the user selected, is there anyway to obtain the string that matches to that id???
Thanks!
D.Gómez
You can use the loadLabel(PackageManager) method of ResolveInfo to get the label of an activity. Here's a full example which finds all the launcher activities on the device and prints them to logcat:
// Get the package manager
PackageManager pm = getPackageManager();
// Create an intent that matches all launcher activities
// (and ignores non-launcher activities)
Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
// Get all activites matching the intent
List<ResolveInfo> launchers = pm.queryIntentActivities(launcherIntent, 0);
for(ResolveInfo info : launchers) {
// Get the activity label and print it
CharSequence label = info.loadLabel(pm);
Log.v("LabelTest", "App found: " + label);
}
To answer the second part of your question too, about accessing the resources of an application: They can be accessed by calling getPackageManager().getResourcesForApplication(String), which will return a Resources object that you can use, though in your case, that should not be necessary.
How, in android, do I start an app set as the default (i.e. Handcent for Messaging, Dolphin for browsing)?
I can only find how to use definite package names for intents:
Intent i = new Intent(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_LAUNCHER);
switch (position) {
case 0: //messages
i.setPackage("com.android.mms");
break;
case 1: //inbox
i.setPackage("com.android.email");
break;
case 2: //browser
i.setPackage("com.android.browser");
default:
i = null;
}
How, in android, do I start an app set as the default (i.e. Handcent for Messaging, Dolphin for browsing)?
"Default" is for a specific operation (e.g., sending a message), not for some abstract notion of "Messaging" in general.
Also, the code you are showing above uses things that are not in the SDK (namely, specific packages). Your code will break on some devices, where the device manufacturer has replaced the app. Your code may break in future versions of Android, when the stock apps are refactored or otherwise renamed.
I think you need to reconsider what it is you are trying to accomplish.
You can search for apps that satisfy a given Intent (e.g., ACTION_SEND), decide which one you want, retrieve its component name, and then launch it with a different Intent that specifies the component name.
Start with:
Intent intent = new Intent(...);
List<ResolveInfo> list = getPackageManager().queryIntentActivities(
intent, PackageManager.MATCH_DEFAULT_ONLY);