I'm working on an app that extends the functionality of another, existing app. I want to know what the easiest way is to determine, through code, whether the first app is installed, preferably by referencing it by com.whoever.whatever but almost any criteria would be helpful.
android.content.pm.PackageManager mPm = getPackageManager(); // 1
PackageInfo info = mPm.getPackageInfo(pName, 0); // 2,3
Boolean installed = info != null;
Used in an activity, you need a context to get the PackageManager
Throws PackageManager.NameNotFoundException, I guess. check!
pName is something like 'com.yourcompany.appname', the same as the value of 'package' in the manifest of the app
The recommended way is to check whether the other application publishes an Intent. Most Intent are not owned by a particular app, so, say, if you're looking for a program that publishes "sending mail" intent, the program that gets opened may be Gmail application or Yahoo Mail application, depending on the user's choice and what was installed in the system.
You may want to look at this: http://developer.android.com/guide/topics/intents/intents-filters.html
Starting Android 12, this requires android.permission.QUERY_ALL_PACKAGES permission, which Google Play may or may not allow you to have
See more details https://developer.android.com/training/package-visibility
Related
Background
The normal way to call for the uninstallation an app is simply by using the "ACTION_DELETE" intent :
startActivity(new Intent(Intent.ACTION_DELETE, Uri.parse("package:" +packageName)));
The problem
starting with some Android version (don't remember which) , apps can be installed for multiple users on the same device.
This means there is a new way to uninstall an app, one which will uninstall it for all users (image taken from Lollipop - Android 5.0 ) :
The question
I've searched in the documentation, but couldn't find the answer those questions:
Is there any way to perform this operation via an intent? Maybe something to add to the intent I've written above ?
Does ADB have a new command to remove an app for all users?
Is there a way to check if an app is installed for multiple users?
Is there any way to perform this operation via an intent? Maybe
something to add to the intent I've written above ?
Yes, but be careful. You can pass in Intent.EXTRA_UNINSTALL_ALL_USERS.
However, it's hidden because it:
should not be part of normal application flow
You could just pass in the constant anyway, if you feel it's necessary and disagree with Google on that one. Just for example, here are the differences between passing in false and true with that constant
final Uri packageURI = Uri.parse("package:" + "some.package.name");
final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
uninstallIntent.putExtra("android.intent.extra.UNINSTALL_ALL_USERS", false or true);
startActivity(uninstallIntent);
Results
Does ADB have a new command to remove an app for all users?
No, the command remains the same.
`adb uninstall 'some.package.name'`
This will remove that app for all users. I'm unaware of a way to specify a particular user.
Is there a way to check if an app is installed for multiple users?
No, not that I'm aware of. In fact, when the Settings apps decides to place the "Uninstall for all users" option in the options menu, it's basically doing so based on whether or not there are multiple users period, not if both the current user and another user have an app installed.
Not to mention, most of the methods in UserManager that you'd need to even tell if there are multiple users on the device, like UserManager.getUserCount, require the MANAGE_USERS permission which is a system API and hidden. So, I'm not even sure why that's a public method.
Also, you can easily test all of your questions, much like I did, by creating a dummy user on your device. You don't even need to log into a Google account.
I want to prevent launching of task manager and Settings applications in my application. For this, I tried to obtain currently running application and checked whether their package name is allowed or not .If it is not allowed then show a new activity.
When work out it is show that the package name of default android Settings application is com.android.settings. Now I have some doubts
Is the Settings application has package name com.android.settings in all android versions? If not, which are they?
How to find package name of Task Manager?
try this
private String querySettingPkgName() {
Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfos == null || resolveInfos.size() == 0) {
return "";
}
return resolveInfos.get(0).activityInfo.packageName;
}
For this,I tried to obtain currently running application and checked whether their package name is allowed or not .If it is not allowed then show a new activity.
Fortunately, for the users affected by your app, this will be unreliable.
Is the Settings application has package name com.android.settings in all android versions?
Not necessarily. More importantly, any given firmware can have any number of applications that modify settings, supplied by the firmware author. Some settings can be modified even without being part of the firmware, particularly on rooted devices.
If not,which are they?
You are welcome to make a list of all device manufacturers and ROM mod authors and ask them that question.
How to find package name of Task Manager?
There are any number of "task manager" apps included in devices, ROM mods, and available on the Play Store and other distribution points. You are welcome to make a list of all of them and ask their authors that question.
shell into the device using adb, and invoke:
pm list packages
this will provide you a list of pacakges. from there you will should see:
com.android.settings
final PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo packageInfo : packages) {
Log.d("Packages", "" + packageInfo.packageName);
}
above code should help you
It's not totally clear what is the scenario.
I guess it is something along the lines of showing off devices to public but not have them f'up the device for others.
Maybe it would be better to do a whitelist instead of a blacklist. Meaning the shop should state which apps should be testable on the devices and then you start your activity if it is any other.
But this again will need maintenance: package names of popular apps may also change. You better provide a way of updating the settings of your app via an online service so you can change the needed packages without physical access to the devices and without having to download and install the complete app.
If you just need a device that goes through many hands and should not be tempered with I suggest using a modified device. I only know of Sonim: they provide a library (needs a Sonim provided hash key in your manifest to use that). With it you can prohibit the altering of many settings without preventing access to the whole settings app.
I've searched and cannot find this, though mainly a challenge of knowing what to search for, I'm sure it's been asked before.
How does an app deduce whether it is running on a "Google Android" device, or an AOSP device (e.g. Kindle Fire etc)?
You can try to enumerate an existing accounts on device using AccountManager class: getAccountsByType(), passing com.google as desired account type. If there is no accounts of this type, this mean either it is AOSP device or the user didn't create Google account yet.
If it is not enough for you, you can use PackageManager class and query some Google-specific package using getPackageInfo() method. For example, com.android.vending - Google Play app.
But pay attention that no one of these methods can guaranty you that target device is running AOSP.
This is not trivial, but most apps do not need to even care. If you do, then it usually means you need some unique features therefore it's simpler to check if certain feature is present or not, instead of what label is on the device. You may also check for presence of certain packages (like Google Play), but lack of it does not mean automatically it is i.e. Fire.
You can check whether you can open market urls by using an Intent.
Documentation on the intent's extras is here.
The Intent would look like this:
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=com.example.android"));
//you can use any package identifier for the check.
startActivity(intent);
} catch (ActvitiyNotFoundException anfe) {
//There's no market installed.
//So you can guess that you're not on a device with Google experience
}
The downside of this method would be, that the user will be taken to the Play Store if it's available.
I'm new to Android developement (I know very basic stuffs), and there is a chance that soon I'll be tasked with porting a WP7 app to Android (fortunately, I can use MonoDroid...).
Now that app has a trial functionality (see here), which for WP7 means that I can check whether the user bought it (so I can enable additional features inside the app) or downloaded the free edition. I do not want the trial to expire, I want a "free edition" of my app to be limited to certain features.
Is there anything similiar for Android? (And can it be done on MonoDroid?)
I've looked at Google Licensing Service, but I don't see how that helps me.
I would go for two apps solution. One "real" application, which contains all the functionality. Second "key" application which only check licensing.
First application will check if the key application is installed. If the check is positive then display full content, enable all features. If the key application is missing the application behaves like free version.
It is also very important to check if the private key that signed both applications is the same. Without this check someone might create their own key application and unlock your functionality. To do so consider this snippet, which I took from this blog: http://www.yoki.org/2010/07/31/creating-a-freepaid-app-pair-for-the-android-market/
protected boolean isProInstalled(Context context) {
// the packagename of the 'key' app
String proPackage = "org.yoki.android.pkgname";
// get the package manager
final PackageManager pm = context.getPackageManager();
// get a list of installed packages
List<PackageInfo> list =
pm.getInstalledPackages(PackageManager.GET_DISABLED_COMPONENTS);
// let's iterate through the list
Iterator<PackageInfo> i = list.iterator();
while(i.hasNext()) {
PackageInfo p = i.next();
// check if proPackage is in the list AND whether that package is signed
// with the same signature as THIS package
if((p.packageName.equals(proPackage)) &&
(pm.checkSignatures(context.getPackageName(), p.packageName) == PackageManager.SIGNATURE_MATCH))
return true;
}
return false;
}
This approach gives you few advantages in flexibility:
separate paid areas. You can assign sets of features to different key applications. eg. app key1 unlocks additional game levels a1,a2,a3 and app key2 unlocks levels b1,b2
time licensing - instead of only checking the existence of key application. You can query it to check if the licence is still valid. That way you can achieve time licences.
Probably the best way for you would be to use in-app purchases
Not all phones have Android Market installed, and therefore using intent to open market app fails.
What's the best way to handle this?
Hide this feature if user doesn't have Android Market installed (how would I detect this?).
Handle the possible error, how (and possibly suggest that the user downloads the Android Market)?
The problem with the answer above is that if you just pass a URL the user will be prompted how to handle the Intent.
A more graceful way to do it IMO, expanding upon the 1st answer above, is to test whether the market app is installed, and then if not, pass a URL (which actually you would then want to test to see if something can handle that intent, but if you happen to have a device without both the play store and a browser then I would question why the user would have my app installed in the first place (another story I suppose)....
Perhaps there is a better way, but here's what works for me:
private String getMarketURI(String marketURL) {
String returnURL = "";
PackageManager packageManager = getApplication().getPackageManager();
Uri marketUri = Uri.parse("market://" + marketURL);
Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri);
if (marketIntent.resolveActivity(packageManager) != null) {
returnURL = "market://" + marketURL;
} else {
returnURL = "https://play.google.com/store/apps/" + marketURL;
}
return returnURL;
}
And then use it like so:
marketIntent.setData(Uri.parse(getMarketURI("details?id=com.yourapps.packagename")));
If your app is being provided by Android Market, then it does have Android Market installed. :)
Okay that is snide, but there is an important truth -- Google goes to a lot of effort to enforce compatibility guarantees on devices for them to be allowed to ship with Android Market, so that is how you can know that whatever you are running on will behave as it should.
If you are delivering your app from something besides Android Market, you need to get information from whoever is delivering the app about what compatibility guarantees they have.
If they don't have compatibility guarantees (or you are just putting a raw .apk up on a web site or such), then you have a complete crap shoot. The device you are running on could have had its software modified in pretty much any way, and have any kind of differences in behavior you can imagine.
That said, if you want to determine whether there is an activity on the current device to handle a particular Intent, you can use this: PackageManager.resolveActivity
Use the web address as the intent target and then if there is no android market it will open in a browser.