i'm using this snippet to check if an app/activity is installed:
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
public static boolean isScanAvailable(Context context) {
return isIntentAvailable(context, "com.google.zxing.client.android.SCAN");
}
In the above example it checks if the Barcode Scanner App is installed, which works just fine.
However, if i try to check for the Adobe Flashplayer using com.adobe.flashplayer it doesn't work and always returns false.
Is there a better / more reliable method to check for Flash?
Uhm yeah. My code posted above does Intent checking which isn't working for the flashplayer (no public intents i guess).
The more obvious way would be to just use getPackageInfo() which works just fine:
public static boolean isFlashAvailable(Context context) {
String mVersion;
try {
mVersion = context.getPackageManager().getPackageInfo(
"com.adobe.flashplayer", 0).versionName;
Log.d("Flash", "Installed: " + mVersion);
return true;
} catch (NameNotFoundException e) {
Log.d("Flash", "Not installed");
return false;
}
}
(As an added bonus we get the exact version number too)
Related
In my application I want to change the password of the lock screen programmatically. So I wrote this method to reset the password:
#TargetApi(26)
private void changePasswordWithToken() {
SecureRandom secureRandom = new SecureRandom();
byte[] token = secureRandom.generateSeed(32);
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getApplicationContext().getSystemService(
DEVICE_POLICY_SERVICE);
if (devicePolicyManager != null) {
devicePolicyManager.setResetPasswordToken(compName, token);
devicePolicyManager.resetPasswordWithToken(compName, "1234", token, 0);
}
}
When I call the method I get this error on my device running Android 9 SDK 27
va.lang.SecurityException: Admin ComponentInfo{com.xxx.xxx/com.xxx.xxxx.MyAdmin} does not own the profile
at android.os.Parcel.createException(Parcel.java:1942)
at android.os.Parcel.readException(Parcel.java:1910)
at android.os.Parcel.readException(Parcel.java:1860)
at android.app.admin.IDevicePolicyManager$Stub$Proxy.setResetPasswordToken(IDevicePolicyManager.java:9995)
at android.app.admin.DevicePolicyManager.setResetPasswordToken(DevicePolicyManager.java:3091)
at com.ssaurel.lockdevice.MainActivity.changePasswordWithToken(MainActivity.java:136)
at com.xx.xx.MainActivity.onClick(MainActivity.java:93)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
...
Before I call this method, I' getting the device admin permissions with this method
private void provisionDeviceAdmin() {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(EXTRA_DEVICE_ADMIN, compName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
"Additional text explaining why we need this permission");
startActivityForResult(intent, RESULT_ENABLE);
}
My policies looks like this
<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<force-lock />
<reset-password />
</uses-policies>
</device-admin>
Please have a look at the documentation, it clearly states there that:
Called by device or profile owner to force set a new device unlock
password or a managed profile challenge on current user. This takes
effect immediately.
From your code it does not look as if you are a "device or profile owner"; please don't mix that up with "device admin" which your apps seems to be (or is trying to get not sure if it is really successful from your code).
For me this solution worked out:
At first, define admin device permissions
policies.xml
<?xml version="1.0" encoding="utf-8"?>
<device-admin>
<uses-policies>
<reset-password/>
</uses-policies>
</device-admin>
Then create a class that extend DeviceAdminReceiver
public class MyAdmin extends DeviceAdminReceiver {
#Override
public void onEnabled(Context context, Intent intent) {
Toast.makeText(context, "Device Admin : enabled", Toast.LENGTH_SHORT).show();
}
#Override
public void onDisabled(Context context, Intent intent) {
Toast.makeText(context, "Device Admin : disabled", Toast.LENGTH_SHORT).show();
}
/**
* Generates a {#link ComponentName} that is used throughout the app.
* #return a {#link ComponentName}
*/
public static ComponentName getComponentName(Context context) {
return new ComponentName(context.getApplicationContext(), MyAdmin.class);
}
}
Get admin device permission with this function in your MainActivity
private void provisionDeviceAdmin() {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(EXTRA_DEVICE_ADMIN, MyAdmin.getComponentName(this));
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
"Additional text explaining why we need this permission");
startActivityForResult(intent, RESULT_ENABLE);
}
Then provision a working profile for your app in the MainActivity
private void provisionManagedProfile() {
Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);
// Use a different intent extra below M to configure the admin component.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//noinspection deprecation
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
MyAdmin.getComponentName(this));
} else {
final ComponentName component = new ComponentName(this,
MyAdmin.class.getName());
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
component);
}
if (intent.resolveActivity(this.getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_PROVISION_MANAGED_PROFILE);
this.finish();
} else {
Toast.makeText(this, "Device provisioning is not enabled. Stopping.",
Toast.LENGTH_SHORT).show();
}
}
After that set the application package to the working profile
private void setAppEnabled(String packageName, boolean enabled) {
PackageManager packageManager = getPackageManager();
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
try {
int packageFlags;
if(Build.VERSION.SDK_INT < 24){
//noinspection deprecation
packageFlags = PackageManager.GET_UNINSTALLED_PACKAGES;
}else{
packageFlags = PackageManager.MATCH_UNINSTALLED_PACKAGES;
}
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
packageFlags);
// Here, we check the ApplicationInfo of the target app, and see if the flags have
// ApplicationInfo.FLAG_INSTALLED turned on using bitwise operation.
if (0 == (applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)) {
// If the app is not installed in this profile, we can enable it by
// DPM.enableSystemApp
if (enabled) {
devicePolicyManager.enableSystemApp(MyAdmin.getComponentName(this), packageName);
} else {
// But we cannot disable the app since it is already disabled
Log.e("TAG", "Cannot disable this app: " + packageName);
return;
}
} else {
// If the app is already installed, we can enable or disable it by
// DPM.setApplicationHidden
devicePolicyManager.setApplicationHidden(
MyAdmin.getComponentName(this), packageName, !enabled);
}
Toast.makeText(this, enabled ? "Enabled" : "Disabled",
Toast.LENGTH_SHORT).show();
} catch (PackageManager.NameNotFoundException e) {
Log.e("TAG", "The app cannot be found: " + packageName, e);
}
}
Create a method to generate random password tokens
private byte[] generateRandomPasswordToken() {
try {
return SecureRandom.getInstance("SHA1PRNG").generateSeed(32);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
Finally implement this method to reset the lock screen password with token
#TargetApi(26)
private void changePasswordWithToken() {
byte[] token = generateRandomPasswordToken();
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getApplicationContext().getSystemService(
DEVICE_POLICY_SERVICE);
KeyguardManager keyguardManager = (KeyguardManager) this.getSystemService(KEYGUARD_SERVICE);
keyguardManager.createConfirmDeviceCredentialIntent(null, null);
if (devicePolicyManager != null) {
devicePolicyManager.setResetPasswordToken(MyAdmin.getComponentName(this), token);
devicePolicyManager.resetPasswordWithToken(MyAdmin.getComponentName(this), "1234", token, 0);
}
}
I have an activity that loads an external url into a webview within my app. I'd like to use Chrome Custom tabs when it's available but I support devices that might not have a version of Chrome that supports them.
In the case of CustomTabs not being supported I'd like to use my old code but use the CustomTabsIntent.Builder() when they are. The old code loads the content in a WebView contained in an Activity where I can still manage the ActionBar.
I'd like to write a helper method that will tell me if it's supported but I'm not sure how. The info on the developer page is pretty slim:
https://developer.chrome.com/multidevice/android/customtabs
It says if you bind succeeds the custom tabs can be safely used. Is there an easy way to bind to test this?
Like this I assume:
Intent serviceIntent = new Intent("android.support.customtabs.action.CustomTabsService");
serviceIntent.setPackage("com.android.chrome");
boolean customTabsSupported = bindService(serviceIntent, new CustomTabsServiceConnection() {
#Override
public void onCustomTabsServiceConnected(final ComponentName componentName, final CustomTabsClient customTabsClient) {}
#Override
public void onServiceDisconnected(final ComponentName name) {}
},
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
if (customTabsSupported) {
// is supported
}
Instead of binding and unbinding the service, you can use the PackageManager to check if Custom Tabs is supported.
private static final String SERVICE_ACTION = "android.support.customtabs.action.CustomTabsService";
private static final String CHROME_PACKAGE = "com.android.chrome";
private static boolean isChromeCustomTabsSupported(#NonNull final Context context) {
Intent serviceIntent = new Intent(SERVICE_ACTION);
serviceIntent.setPackage(CHROME_PACKAGE);
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentServices(serviceIntent, 0);
return !(resolveInfos == null || resolveInfos.isEmpty());
}
Be aware that other browsers may support Custom Tabs in the future, so you may want to modify that to support this case.
You can try following code to figure out if you have a browser that supports custom tab:
private static final String TAG = "CustomTabLauncher";
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
String mPackageNameToUse;
private String getPackageName(Context context) {
if (mPackageNameToUse != null) {
return mPackageNameToUse;
}
// Get default VIEW intent handler that can view a web url.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.test-url.com"));
// Get all apps that can handle VIEW intents.
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
mPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
mPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
mPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
mPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
mPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
mPackageNameToUse = LOCAL_PACKAGE;
}
return mPackageNameToUse;
}
When calling, you can do something like this:
public void openCustomTab(Uri uri, Activity activity) {
//If we cant find a package name, it means there's no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the default browser
if (getPackageName(activity) == null) {
activity.startActivity(new Intent(Intent.ACTION_VIEW, uri));
} else {
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
intentBuilder.enableUrlBarHiding();
intentBuilder.setToolbarColor(activity.getResources().getColor(R.color.purple_a_01));
CustomTabsIntent customTabsIntent = intentBuilder.build();
customTabsIntent.intent.setPackage(mPackageNameToUse);
customTabsIntent.launchUrl(activity, uri);
}
}
I ended up writing a static method in my Utils class so I can check and handle the case where it isn't supported:
/**
* Check if Chrome CustomTabs are supported.
* Some devices don't have Chrome or it may not be
* updated to a version where custom tabs is supported.
*
* #param context the context
* #return whether custom tabs are supported
*/
public static boolean isChromeCustomTabsSupported(#NonNull final Context context) {
Intent serviceIntent = new Intent("android.support.customtabs.action.CustomTabsService");
serviceIntent.setPackage("com.android.chrome");
CustomTabsServiceConnection serviceConnection = new CustomTabsServiceConnection() {
#Override
public void onCustomTabsServiceConnected(final ComponentName componentName, final CustomTabsClient customTabsClient) { }
#Override
public void onServiceDisconnected(final ComponentName name) { }
};
boolean customTabsSupported =
context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
context.unbindService(serviceConnection);
return customTabsSupported;
}
I solved this problem by handling ActivityNotFound exception in catch block.
The trick is to check if the browser activity of chrome can be started or not, if it can't be started or throws an exception then simply open the link through Intent.ACTION_VIEW.
Here is all the relevant code ....
private void onBtnLinkClicked(View v, int pos) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
openCustomTab(url);
} else {
openBrowserActivity(url);
}
} catch (Exception e) {
e.printStackTrace();
openBrowserActivity(url);
}
}
private void openBrowserActivity(String url) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(browserIntent);
}
What is openCustomTab(url) you say :
Here is the relevant code for it.
private void openCustomTab(String url) {
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
int color = context.getResources().getColor(R.color.colorPrimary);
intentBuilder.setToolbarColor(color);
intentBuilder.setShowTitle(true);
String menuItemTitle = context.getString(R.string.menu_title_share);
PendingIntent menuItemPendingIntent = createPendingShareIntent();
intentBuilder.addMenuItem(menuItemTitle, menuItemPendingIntent);
intentBuilder.setStartAnimations(context,
R.anim.slide_in_right, R.anim.slide_out_left);
intentBuilder.setExitAnimations(context,
android.R.anim.slide_in_left, android.R.anim.slide_out_right);
CustomTabActivityHelper.openCustomTab(
activity, intentBuilder.build(), Uri.parse(url), new WebviewFallback());
}
My style of answer may seem cocky but before clicking downvote let me know if you have run into any unexpected bug or any other problem that this approach may have cause. Do give your feedback, we are a community afterall.
New issues may arise if you are targeting API level 30 and running on an Android 11 device.
You will need to add a section to your manifest, such as:
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
OR
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
Without this, some of the calls to PackageManager mentioned in the above posts will not work as you expect.
https://developer.android.com/training/package-visibility/use-cases
From developer site of Chrome, I found the followings -
As of Chrome 45, Chrome Custom Tabs is now generally available to all
users of Chrome, on all of Chrome's supported Android versions
(Jellybean onwards).
Link: https://developer.chrome.com/multidevice/android/customtabs#whencaniuseit
So, I checked whether Chrome supports Chrome Custom Tab by version.
Check my code:
String chromePackageName = "com.android.chrome";
int chromeTargetVersion = 45;
boolean isSupportCustomTab = false;
try {
String chromeVersion = getApplicationContext().getPackageManager().getPackageInfo(chromePackageName, 0).versionName;
if(chromeVersion.contains(".")) {
chromeVersion = chromeVersion.substring(0, chromeVersion.indexOf('.'));
}
isSupportCustomTab = (Integer.valueOf(chromeVersion) >= chromeTargetVersion);
} catch (PackageManager.NameNotFoundException ex) {
} catch (Exception ex) { }
if (isSupportCustomTab) {
//Use Chrome Custom Tab
} else {
//Use WebView or other Browser
}
I don't know how efficient it is, just want to share.
I've already done this for iPhone app detection, using the meta tag described here:
http://developer.apple.com/library/ios/#documentation/AppleApplications/Reference/SafariWebContent/PromotingAppswithAppBanners/PromotingAppswithAppBanners.html
Now I'm trying to do the same for Droid-app sniffing. Basically, I want to check if the user has the 'sniffing' app installed or not.
How do I detect whether or not a smartphone has an Android app installed?
In order to make code work change "com.your.package.appname.id" with the application id.
id is the app package and also the market url eg. gmail app url at the market is https://play.google.com/store/apps/details?id=com.google.android.gm and the id/packagename is com.google.android.gm
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
public class Example extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Put the package name here...
boolean installed = appInstalledOrNot("com.your.package.appname.id");
if(installed)
{
System.out.println("App already installed om your android");
}
else
{
System.out.println("App is not installed om your android");
}
}
private boolean appInstalledOrNot(String uri)
{
PackageManager pm = getPackageManager();
boolean app_installed = false;
try
{
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
app_installed = true;
}
catch (PackageManager.NameNotFoundException e)
{
app_installed = false;
}
return app_installed ;
}
}
From this: How to get a list of installed android applications and pick one to run
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List pkgAppsList = context.getPackageManager().queryIntentActivities( mainIntent, 0);
Now, from pkgAppList you can see if the app is installed
I mean, how can I check that user's device is not a tablet (or music player) without dialer?
P.S.: My app uses numeric codes entered in dialer, so I want to check it's presence.
It is a always a good approach to check if the intent receiver/activity actually exists before you attempt to invoke it.
Some of the reasons being:
If a non-existent intent, you application will force close.
If the intent receiver is not present, you might want to redirect the user to the market to download the necessary application.
Depending on the existence of an intent you might want to make menu options appear/disappear.
The following snippet contains two functions: isIntentAvailable() and isActivityAvailable() ,which can perform the checking and return a boolean accordingly.
public boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List resolveInfo =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo.size() > 0) {
return true;
}
return false;
}
Just pass your action string as a parameter to this function before using it to start any third party application.
Seems to be what I've needed:
public static String TelephonyChecker (Context context) {
TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
if(manager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE){
return "Off";
}else{
return "On";
}
}
Is there a way to check which BroadcastReceivers are declared in the manifest, in runtime?
With PackageManager, you can queryBroadcastReceivers() to find who will all respond to a specific Intent, and with getInstalledPackages(), you can find out the receivers installed per package.
The code would be similar like this, from within an Activity:
// Query all packages that have the BroadcastReceivers...
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = getPackageManager();
final List<ResolveInfo> listPkgs = pm.queryBroadcast(mainIntent, 0);
if (listPkgs != null && listPkgs.size() > 0){
for(ResolveInfo resInfo : listPkgs){
// Now resInfo will contain the list of packages that has receivers...
}
}
Thanks, but was not my intention... I wanted to get know if a specific receiver is declared in the running application in runtime, and achieved it like this:
private <Receiver extends CyborgReceiver<?>> boolean checkIfBroadcastReceiverIsRegisteredInManifest(Class<Receiver> receiverType) {
PackageManager pm = application.getPackageManager();
try {
ActivityInfo info = pm.getReceiverInfo(new ComponentName(application, receiverType), PackageManager.GET_RECEIVERS);
return info.enabled;
} catch (NameNotFoundException e) {
return false;
}
}
Pass in the application object as the first argument, you can do this with (Application)context.GetApplicationContext() if you have to, then pass in your class which implements the broadcast receiver class as the second argument e.g. broadcastReceiver.class
public static boolean validateReceiverInManifest(Application application, Class receiverClass) throws PackageManager.NameNotFoundException {
PackageManager pm = application.getPackageManager();
String packageName = application.getPackageName();
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_RECEIVERS);
ActivityInfo[] receivers = packageInfo.receivers;
String receiverClassName = receiverClass.getName();
for (ActivityInfo activityInfo : receivers) {
if (activityInfo.name.equals(receiverClassName)) {
return true;
}
}
return false;
}