Background
I'm trying to check if an activity (or any other app component type, for that matter) is enabled/disabled at runtime.
The problem
It's possible to use the next code:
final ComponentName componentName = new ComponentName(context, activityClass);
final PackageManager pm = context.getPackageManager();
final int result = pm.getComponentEnabledSetting(componentName);
But the returned result, as written on the documentation is:
Returns the current enabled state for the component. May be one of
COMPONENT_ENABLED_STATE_ENABLED, COMPONENT_ENABLED_STATE_DISABLED, or
COMPONENT_ENABLED_STATE_DEFAULT. The last one means the component's
enabled state is based on the original information in the manifest as
found in ComponentInfo.
So it's not just enabled/disabled, but also "default".
The question
If "COMPONENT_ENABLED_STATE_DEFAULT" is returned, how do I know if it's default as enabled or disabled (at runtime)?
The reason for this question is that the code should work no matter what people put in the manifest (for the "enabled" attribute) .
Is it possible perhaps to use intents resolving?
If COMPONENT_ENABLED_STATE_DEFAULT is returned, how do I know if it's default as enabled or disabled?
You will need to load all the components using PackageManager and check the enabled state for the matching ComponentInfo. The following code should work:
public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
ComponentName componentName = new ComponentName(pkgName, clsName);
int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);
switch (componentEnabledSetting) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
return false;
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
return true;
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
default:
// We need to get the application info to get the component's default state
try {
PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS
| PackageManager.GET_SERVICES
| PackageManager.GET_PROVIDERS
| PackageManager.GET_DISABLED_COMPONENTS);
List<ComponentInfo> components = new ArrayList<>();
if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities);
if (packageInfo.services != null) Collections.addAll(components, packageInfo.services);
if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers);
for (ComponentInfo componentInfo : components) {
if (componentInfo.name.equals(clsName)) {
return componentInfo.isEnabled();
}
}
// the component is not declared in the AndroidManifest
return false;
} catch (PackageManager.NameNotFoundException e) {
// the package isn't installed on the device
return false;
}
}
}
Testing the above code on my device:
System.out.println(isComponentEnabled(getPackageManager(),
"com.android.systemui",
"com.android.systemui.DessertCaseDream"));
System.out.println(isComponentEnabled(getPackageManager(),
"com.android.settings",
"com.android.settings.DevelopmentSettings"));
false
true
I think the field ComponentInfo.enabled means the value set in the AndroidManifest.xml file. If not specified, the default will be true. So, in the following example:
<receiver android:name=".BroadcastReceiver1"
android:enabled="false" />
<receiver android:name=".BroadcastReceiver2"
android:enabled="true" />
<receiver android:name=".BroadcastReceiver3" />
the enabled field will be false in BroadcastReceiver1, true in BroadcastReceiver2 and true in BroadcastReceiver3, while pm.getComponentEnabledSetting(componentName) will return ENABLED or DISABLED if the value in AndroidManifest is overridden or DEFAULT if not overriden
Then, according to #Jared Rummler answer if pm.getComponentEnabledSetting(componentName) returns PackageManager.COMPONENT_ENABLED_STATE_DEFAULT the actual value will be the one set in ComponentInfo.enabled field which will be the same as the one set in AndroidManifest, while ComponentInfo.isEnabled() would depend on the whole application state
EDIT: To sum up:
public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
ComponentName componentName = new ComponentName(pkgName, clsName);
int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);
switch (componentEnabledSetting) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
return false;
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
return true;
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
default:
// We need to get the application info to get the component's default state
try {
PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS
| PackageManager.GET_SERVICES
| PackageManager.GET_PROVIDERS
| PackageManager.GET_DISABLED_COMPONENTS);
List<ComponentInfo> components = new ArrayList<>();
if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities);
if (packageInfo.services != null) Collections.addAll(components, packageInfo.services);
if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers);
for (ComponentInfo componentInfo : components) {
if (componentInfo.name.equals(clsName)) {
return componentInfo.enabled; //This is the default value (set in AndroidManifest.xml)
//return componentInfo.isEnabled(); //Whole package dependant
}
}
// the component is not declared in the AndroidManifest
return false;
} catch (PackageManager.NameNotFoundException e) {
// the package isn't installed on the device
return false;
}
}
}
The code of Jared Rummler is good and it work but it takes the status in the manifest.
To have the current status of a component inside my app I needed to create the "ComponentName" through the "Package Context" and the "Component Class" and not the "Package Name" and "Class Name".
I had a BroadcastReceiver setted to "enabled = false" in the manifest, after that I enabled it inside my app using the package manager, but the Jared Rummler's codes always return me "STATE_ENABLED_DEFAULT" and "enabled and isEnabled()" always returned false.
Using the "Package Context" and the "Component Class" i get directly the "ENABLED_STATE_DISABLED" and "ENABLED_STATE_ENABLED" without using the code in the default part, also the "enabled and isEnabled()" returned me anyway FALSE if I've started the receiver using the package manager.
Hope this is useful, see u
public static void enableDisableComponent(Context pckg, Class componentClass, boolean enable){
ComponentName component = new ComponentName(pckg, componentClass);
if(enable == !checkIfComponentIsEnabled(pckg, componentClass)) {
pckg.getPackageManager().setComponentEnabledSetting(
component,
enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
);
}
}
public static boolean checkIfComponentIsEnabled(Context pckg, Class componentClass){
boolean ret = false;
ComponentName componentName = new ComponentName(pckg, componentClass);
int componentEnabled = pckg.getPackageManager().getComponentEnabledSetting(componentName);
switch(componentEnabled){
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
break;
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
ret = true;
break;
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
default:
ret = checkEnabledComponentsInfo(pckg, componentClass);
break;
}
return ret;
}
private static boolean checkEnabledComponentsInfo(Context pckg, Class componentClass){
boolean ret = false;
try{
PackageInfo packageInfo = pckg.getPackageManager().getPackageInfo(
pckg.getPackageName(),
PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS |
PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS |
PackageManager.GET_DISABLED_COMPONENTS
);
List<ComponentInfo> componentsInfo = new ArrayList<>();
if(packageInfo.activities != null && packageInfo.activities.length > 0){
Collections.addAll(componentsInfo, packageInfo.activities);
}
if(packageInfo.services != null && packageInfo.services.length > 0){
Collections.addAll(componentsInfo, packageInfo.services);
}
if(packageInfo.providers != null && packageInfo.providers.length > 0){
Collections.addAll(componentsInfo, packageInfo.providers);
}
if(packageInfo.receivers != null && packageInfo.receivers.length > 0){
Collections.addAll(componentsInfo, packageInfo.receivers);
}
if(componentsInfo.size() > 0){
for(ComponentInfo info : componentsInfo){
if(info.name.equals(componentClass.getName())){
ret = info.isEnabled();
break;
}
}
}
} catch(PackageManager.NameNotFoundException nnfE){
onException(nnfE);
}
return ret;
}
Related
In my app I need to monitorize recently added or updated packages, but since Oreo this is a hard task.
To do it I have a service that runs every X time to detect the new installed/updated apps.
The main core of this service is to call the getChangedPackages function from the PackageManager, but this function always returns null, even if I install or update any app from or not from the Play Store in the interval between two consequtive calls to getChangedPackages.
https://developer.android.com/reference/android/content/pm/PackageManager.html#getChangedPackages(int)
I need to request any permission to call this function? Is the getChangedPackages buggy?
private void _doProcess()
{
try
{
PackageManager package_manager = getPackageManager();
int sequence_number = ApplicationPreferences.getInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, 0);
ChangedPackages changed_packages = package_manager.getChangedPackages(sequence_number);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages == null) changed_packages = package_manager.getChangedPackages(0);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages != null)
{
List<String> packages_names = changed_packages.getPackageNames();
LogUtilities.show(this, String.format("%d recently installed/updated apps", packages_names == null ? 0 : packages_names.size()));
if (packages_names != null) for (String package_name : packages_names) PackagesUpdatedReceiver.doProcessPackageUpdate(this, new Intent(isNewInstall(package_manager, package_name) ? Intent.ACTION_PACKAGE_ADDED : Intent.ACTION_PACKAGE_REPLACED).setData(Uri.parse(String.format("package:%s", package_name))));
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", changed_packages.getSequenceNumber()));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, changed_packages.getSequenceNumber());
}
else
{
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", sequence_number + 1));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, sequence_number + 1);
}
}
catch (Exception e)
{
LogUtilities.show(this, e);
}
}
My experimental results so far have shown that this PackageManager API method getChangedPackages() is not reliable: quite often the returned ChangedPackages value contains many unchanged packages. So I’ve decided to implement a similar feature in a class called PackageUtils, as shown below. The idea is to poll for all the installed packages, as shown in method getInstalledPackageNames() below, and compare the string list with a previously saved one. This comparison boils down to comparing 2 string lists, as shown in method operate2StringLists() below. To get a set of removed packages, use GET_1_MINUS_2_OR_REMOVED as operation. To get a set of added packages, use GET_2_MINUS_1_OR_ADDED as operation.
public class PackageUtils {
public static final int GET_1_MINUS_2_OR_REMOVED = 0;
public static final int GET_2_MINUS_1_OR_ADDED = 1;
// Get all the installed package names
public static List<String> getInstalledPackageNames(Context context) {
List<String> installedPackageNames = new ArrayList<>();
try {
PackageManager packageManager = context.getPackageManager();
List<ApplicationInfo> appInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo appInfo : appInfoList) {
installedPackageNames.add(appInfo.packageName);
}
} catch (Exception e) {
e.printStackTrace();
}
return installedPackageNames;
}
// Compare 2 string lists and return differences.
public static Set<String> operate2StringLists(List<String> pkgList1, List<String> pkgList2, int operation) {
Set<String> result = null;
Set<String> pkgSet1 = new HashSet<String>(pkgList1);
Set<String> pkgSet2 = new HashSet<String>(pkgList2);
switch (operation) {
case GET_1_MINUS_2_OR_REMOVED:
pkgSet1.removeAll(pkgSet2);
result = pkgSet1;
break;
case GET_2_MINUS_1_OR_ADDED:
pkgSet2.removeAll(pkgSet1);
result = pkgSet2;
break;
default:
break;
}
return result;
}
}
The code has been tested on an Android Oreo device. It can reliably detect all added and removed packages between 2 time instances. However, it can’t detect updated packages in-between.
Finally got it. You have to create a variable called sequenceNumber, and update it every time you query changed packages.
private static int sequenceNumber = 0;
...
PackageManager pm = getContext().getPackageManager();
ChangedPackages changedPackages = pm.getChangedPackages(sequenceNumber);
if(changedPackages != null)
sequenceNumber = changedPackages.getSequenceNumber();
I need to pick contacts in my app and would like to exclude those which are stored in my SIM card. Is it possible with ACTION_PICK?
No, it's not possible
Unfortunately, it's not possible for now.
To proof it, let's dive into source code of ContanctsListActivity.
Here's an onCreate() method of the Activity. In it, ContactApp reads Intent(ACTION_PICK) we passing to it and handles it respectively:
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mIconSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
mContactsPrefs = new ContactsPreferences(this);
mPhotoLoader = new ContactPhotoLoader(this, R.drawable.ic_contact_list_picture);
// Resolve the intent
final Intent intent = getIntent();
// Allow the title to be set to a custom String using an extra on the intent
String title = intent.getStringExtra(UI.TITLE_EXTRA_KEY);
if (title != null) {
setTitle(title);
}
String action = intent.getAction();
String component = intent.getComponent().getClassName();
// When we get a FILTER_CONTACTS_ACTION, it represents search in the context
// of some other action. Let's retrieve the original action to provide proper
// context for the search queries.
if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
mSearchMode = true;
mShowSearchSnippets = true;
Bundle extras = intent.getExtras();
if (extras != null) {
mInitialFilter = extras.getString(UI.FILTER_TEXT_EXTRA_KEY);
String originalAction =
extras.getString(ContactsSearchManager.ORIGINAL_ACTION_EXTRA_KEY);
if (originalAction != null) {
action = originalAction;
}
String originalComponent =
extras.getString(ContactsSearchManager.ORIGINAL_COMPONENT_EXTRA_KEY);
if (originalComponent != null) {
component = originalComponent;
}
} else {
mInitialFilter = null;
}
}
Log.i(TAG, "Called with action: " + action);
mMode = MODE_UNKNOWN;
if (UI.LIST_DEFAULT.equals(action) || UI.FILTER_CONTACTS_ACTION.equals(action)) {
.....
else if (Intent.ACTION_PICK.equals(action)) {
// XXX These should be showing the data from the URI given in
// the Intent.
final String type = intent.resolveType(this);
if (Contacts.CONTENT_TYPE.equals(type)) {
mMode = MODE_PICK_CONTACT;
} else if (People.CONTENT_TYPE.equals(type)) {
mMode = MODE_LEGACY_PICK_PERSON;
} else if (Phone.CONTENT_TYPE.equals(type)) {
mMode = MODE_PICK_PHONE;
} else if (Phones.CONTENT_TYPE.equals(type)) {
mMode = MODE_LEGACY_PICK_PHONE;
} else if (StructuredPostal.CONTENT_TYPE.equals(type)) {
mMode = MODE_PICK_POSTAL;
} else if (ContactMethods.CONTENT_POSTAL_TYPE.equals(type)) {
mMode = MODE_LEGACY_PICK_POSTAL;
}
....
// VERY LONG IF WITH DIFFERENT MODE-SELECTION
....
}
.....
if (mMode == MODE_JOIN_CONTACT) {
setContentView(R.layout.contacts_list_content_join);
} else if (mSearchMode) {
setContentView(R.layout.contacts_search_content);
} else if (mSearchResultsMode) {
setContentView(R.layout.contacts_list_search_results);
} else {
setContentView(R.layout.contacts_list_content);
}
setupListView();
...
}
It's very long method (and I also suggest to check setupListView() method), but pretty straightforward. And, as you can see, there's no parameter you can pass to specify source of contacts you want to pick from. Only thing you can configure here is the particular mMode ContactsApp to use (MODE_PICK_CONTACT, MODE_PICK_PHONE, etc.) - but unfortunately number of possible modes is very limited by 6 and non of them suits you.
(If anyone needs to pass mMode to ContanctsListActivity - use intent's setType() method, for example: intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);)
Workaround
As a workaround - as tiny sunlight's suggested in comments - render non-SIM contacts within the app and pick the one you needed from there.
How to get all android contacts but without those which are on SIM - this link looks like most promising one explaining how to query cursor with all contacts, apart from SIM ones.
I hope, it helps
Android 4.1 offers the user a check box to disable notifications for a specific application.
However, as a developer we have no way to know whether a call to notify was effective or not.
I really need to check if the notifications are disabled for the current application but I can't find any setting for that in the API.
Is there ever a way to check this setting in the code?
You can't 100% can't.
It is asked in this Google I/O 2012 video and the Project lead for the new notifications declares that you can't.
Edit
2016 update: Now you can check it, as said in this Google I/O 2016 video.
Use NotificationManagerCompat.areNotificationsEnabled(), from support library, to check if notifications are blocked on API 19+. The versions below API 19 will return true (notifications are enabled).
Answer from #blundell is correct but there is a minor change in newer versions.
NotificationManagerCompat.from(context).areNotificationsEnabled()
Actually this is pretty easy to do:
/**
* Created by desgraci on 5/7/15.
*/
public class NotificationsUtils {
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
public static boolean isNotificationEnabled(Context context) {
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
Class appOpsClass = null; /* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (int)opPostNotificationValue.get(Integer.class);
return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
}
If you are using Xamarin and you need this answer you can use this code:
//return true if this option is not supported.
public class NotificationsUtils
{
private const String CHECK_OP_NO_THROW = "checkOpNoThrow";
private const String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
public static bool IsNotificationEnabled(global::Android.Content.Context context) {
AppOpsManager mAppOps = (AppOpsManager) context.GetSystemService(global::Android.Content.Context.AppOpsService);
ApplicationInfo appInfo = context.ApplicationInfo;
String pkg = context.ApplicationContext.PackageName;
int uid = appInfo.Uid;
try {
var appOpsClass = Java.Lang.Class.ForName("android.app.AppOpsManager");
var checkOpNoThrowMethod = appOpsClass.GetMethod(CHECK_OP_NO_THROW,Java.Lang.Integer.Type,Java.Lang.Integer.Type,new Java.Lang.String().Class);//need to add String.Type
var opPostNotificationValue = appOpsClass.GetDeclaredField (OP_POST_NOTIFICATION);
var value = (int)opPostNotificationValue.GetInt(Java.Lang.Integer.Type);
var mode = (int)checkOpNoThrowMethod.Invoke(mAppOps,value, uid, pkg);
return (mode == (int)AppOpsManagerMode.Allowed);
} catch (Exception)
{
System.Diagnostics.Debug.WriteLine ("Notification services is off or not supported");
}
return true;
}
}
It seems like there is no way to query notification state.
I recommend this:
Design you application with notifications.
Let user to disable notifications from application's settings.
Check whether notifications are clicked. If user clicks notification, save this to preferences.
In your app, if notification setting is on, and if user is Android 4.1+ (API 16), but if user doesn't click notification for some days / weeks, assume that user disabled notifications.
Not 100% correct. But this gives an opinion.
For example if user doesn't click any app notification for 10-15 days, probably he disabled it
I use this method to check whether the notifications are enabled or not,
the above-mentioned methods will work for checking whether notifications enabled or not. But from Android 8 onwards for creating notifications we have to create a channel first, so from Oreo, we have to check for your notification channel enabled or not.
/**
* Checking Whether notifications are enabled or not
* #return true if notifications are enabled otherwise false
*/
public static final String CHANNEL_ID = "your_channel_id";
private boolean isNotificationChannelEnabled(){
if(NotificationManagerCompat.from(this).areNotificationsEnabled()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = manager.getNotificationChannel(CHANNEL_ID);
if (channel == null)
return true; //channel is not yet created so return boolean
// by only checking whether notifications enabled or not
return channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
}
return true;
}
return false;
}
Android 4.1 offers the user a check box to disable notifications for a specific application.
However, as a developer we have no way to know whether a call to notify was effective or not.
I really need to check if the notifications are disabled for the current application but I can't find any setting for that in the API.
Is there ever a way to check this setting in the code?
You can't 100% can't.
It is asked in this Google I/O 2012 video and the Project lead for the new notifications declares that you can't.
Edit
2016 update: Now you can check it, as said in this Google I/O 2016 video.
Use NotificationManagerCompat.areNotificationsEnabled(), from support library, to check if notifications are blocked on API 19+. The versions below API 19 will return true (notifications are enabled).
Answer from #blundell is correct but there is a minor change in newer versions.
NotificationManagerCompat.from(context).areNotificationsEnabled()
Actually this is pretty easy to do:
/**
* Created by desgraci on 5/7/15.
*/
public class NotificationsUtils {
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
public static boolean isNotificationEnabled(Context context) {
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
Class appOpsClass = null; /* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (int)opPostNotificationValue.get(Integer.class);
return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
}
If you are using Xamarin and you need this answer you can use this code:
//return true if this option is not supported.
public class NotificationsUtils
{
private const String CHECK_OP_NO_THROW = "checkOpNoThrow";
private const String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
public static bool IsNotificationEnabled(global::Android.Content.Context context) {
AppOpsManager mAppOps = (AppOpsManager) context.GetSystemService(global::Android.Content.Context.AppOpsService);
ApplicationInfo appInfo = context.ApplicationInfo;
String pkg = context.ApplicationContext.PackageName;
int uid = appInfo.Uid;
try {
var appOpsClass = Java.Lang.Class.ForName("android.app.AppOpsManager");
var checkOpNoThrowMethod = appOpsClass.GetMethod(CHECK_OP_NO_THROW,Java.Lang.Integer.Type,Java.Lang.Integer.Type,new Java.Lang.String().Class);//need to add String.Type
var opPostNotificationValue = appOpsClass.GetDeclaredField (OP_POST_NOTIFICATION);
var value = (int)opPostNotificationValue.GetInt(Java.Lang.Integer.Type);
var mode = (int)checkOpNoThrowMethod.Invoke(mAppOps,value, uid, pkg);
return (mode == (int)AppOpsManagerMode.Allowed);
} catch (Exception)
{
System.Diagnostics.Debug.WriteLine ("Notification services is off or not supported");
}
return true;
}
}
It seems like there is no way to query notification state.
I recommend this:
Design you application with notifications.
Let user to disable notifications from application's settings.
Check whether notifications are clicked. If user clicks notification, save this to preferences.
In your app, if notification setting is on, and if user is Android 4.1+ (API 16), but if user doesn't click notification for some days / weeks, assume that user disabled notifications.
Not 100% correct. But this gives an opinion.
For example if user doesn't click any app notification for 10-15 days, probably he disabled it
I use this method to check whether the notifications are enabled or not,
the above-mentioned methods will work for checking whether notifications enabled or not. But from Android 8 onwards for creating notifications we have to create a channel first, so from Oreo, we have to check for your notification channel enabled or not.
/**
* Checking Whether notifications are enabled or not
* #return true if notifications are enabled otherwise false
*/
public static final String CHANNEL_ID = "your_channel_id";
private boolean isNotificationChannelEnabled(){
if(NotificationManagerCompat.from(this).areNotificationsEnabled()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = manager.getNotificationChannel(CHANNEL_ID);
if (channel == null)
return true; //channel is not yet created so return boolean
// by only checking whether notifications enabled or not
return channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
}
return true;
}
return false;
}
I'm new here and I've searched for questions to help me but I have no clear answers.
I need to make an application to block other applications on the phone.
I've seen several on the market but I want to make one.
is there any way of knowing when a user tries to open an application and bring forward an activity? (to put the password).
I tried with FileObserver, but only works with files and directories (obviously).
Could I make a listener that captures the Intent of the other applications before starting?
I apologize for my english and I appreciate your help!
No you cannot know when another application is launched without some kind of hack.
This is because application launches are not broadcasted.
What you can do is creating a service running on fixed intervals , say 1000 milliseconds, that checks what non system application is on front. Kill that app and from the service pop a password input box. If that password is correct relaunch that application
Here is some code sample
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
List<RunningAppProcessInfo> appProcesses= activityManager.getRunningAppProcesses();
for (RunningAppProcessInfo appProcess : appProcesses) {
try {
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
if (!lastFrontAppPkg.equals((String) appProcess.pkgList[0])) {
apkInfo = ApkInfo.getInfoFromPackageName(appProcess.pkgList[0], mContext);
if (apkInfo == null || (apkInfo.getP().applicationInfo.flags && ApplicationInfo.FLAG_SYSTEM) == 1) {
// System app continue;
} else if (((apkInfo.getP().versionName == null)) || (apkInfo.getP().requestedPermissions == null)) {
//Application that comes preloaded with the device
continue;
} else {
lastFrontAppPkg = (String) appProcess.pkgList[0];
}
//kill the app
//Here do the pupop with password to launch the lastFrontAppPkg if the pass is correct
}
}
}
} catch (Exception e) {
//e.printStackTrace();
}
}
}
}, 0, 1000);
And here is the ApkInfo.getInfoFromPackageName()
/**
* Get the ApkInfo class of the packageName requested
*
* #param pkgName
* packageName
* #return ApkInfo class of the apk requested or null if package name
* doesn't exist
* #see ApkInfo
*/
public static ApkInfo getInfoFromPackageName(String pkgName,
Context mContext) {
ApkInfo newInfo = new ApkInfo();
try {
PackageInfo p = mContext.getPackageManager().getPackageInfo(
pkgName, PackageManager.GET_PERMISSIONS);
newInfo.appname = p.applicationInfo.loadLabel(
mContext.getPackageManager()).toString();
newInfo.pname = p.packageName;
newInfo.versionName = p.versionName;
newInfo.versionCode = p.versionCode;
newInfo.icon = p.applicationInfo.loadIcon(mContext
.getPackageManager());
newInfo.setP(p);
} catch (NameNotFoundException e) {
e.printStackTrace();
return null;
}
return newInfo;
}
there is a way to do so . you can know when a application is launched.
you can use packagemanager class to get all the information about any installed and inbuld application . and use the below code to know whwn that application is launched
#Override
public void run() { Log.i("test","detector run");
try {
Process process;
process = Runtime.getRuntime().exec(ClearLogCatCommand);
process = Runtime.getRuntime().exec(LogCatCommand);
br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
// Check if it matches the pattern
while(((line=br.readLine()) != null) && !this.isInterrupted()){
Log.d("Detector", "RUN"+line);
// Ignore launchers
if (line.contains("cat=[" + Intent.CATEGORY_HOME + "]")) continue;
Matcher m = ActivityNamePattern.matcher(line);
if (!m.find()) continue;
if (m.groupCount()<2){
// Log.d("Detector", "Unknown problem while matching logcat output. Might be SDK version?");
continue;
}
if (mListener!=null) mListener.onActivityStarting(m.group(1), m.group(2));
Log.i("Detector", "Found activity launching: " + m.group(1) + " / " + m.group(2));
}
} catch (IOException e) {
e.printStackTrace();
}
}
You can now use an AccessibilityService that allows you to find out which Activity is at the top.
In the AccessibilityService class:
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
ComponentName componentName = new ComponentName(
event.getPackageName().toString(),
event.getClassName().toString()
);
ActivityInfo activityInfo = tryGetActivity(componentName);
boolean isActivity = activityInfo != null;
if (isActivity) {
Log.i("CurrentActivity", componentName.flattenToShortString());
}
}
}
You will have to turn the Accessibility on in your phone's settings, which can be done by going to Settings > Accessibility > Services > Your App. There are also a couple of permissions you'll have to add in your Manifest. A lot of the information can be found in the Android Developers site: http://developer.android.com/reference/android/accessibilityservice/AccessibilityService.html
Hope this helps!