I'm currently developing an app for Android that uses the NotificationListenerService, which requires that the user will enable notification access for my app under Setting -> Security -> Notification Access.
My question is that can I redirect the user to this place so they will enable it? So far I only managed to direct them to Setting -> Security window.
Also, is it possible to first check if the user enabled notification access for my app already and only then redirect them?
You can open the NotificationAccessSettingsActivity by using the following Intent, but I'm not sure about checking to see if they've already enabled your app.
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
Alternatively, for API 22+:
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
Many Thanks to #adneal and #Waboodoo. I am posting this for an complete answer
Check permission granted or not using this method
private boolean isNotificationServiceRunning() {
ContentResolver contentResolver = getContentResolver();
String enabledNotificationListeners =
Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
String packageName = getPackageName();
return enabledNotificationListeners != null && enabledNotificationListeners.contains(packageName);
}
Then show settings activity, if necessary
boolean isNotificationServiceRunning = isNotificationServiceRunning();
if(!isNotificationServiceRunning){
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
}
You can always use the notification manager to check if notifications are enabled at the OS level:
NotificationManagerCompat.from(context).areNotificationsEnabled()
Related
In my app, I am enabling "enabled_notification_listeners" it is working in all cases except one case.
If we run the app the first time I am checking if is it enables or not if not then redirecting a user to the setting of notification access and if I upgrade the app or run the app from the android studio and I am checking that it is enabled or not it gives me it is enabled but when a notification comes I didn't receive any callbacks.
If I manually disable it and enable it, it starts working.
So is there any way that we can disable it programmatically?
Here is how I am opening the setting page:
startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
How I am checking if is it enables or disables:
boolean hasNotificationAccess()
{
ContentResolver contentResolver = this.getContentResolver();
String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
String packageName = this.getPackageName();
return !(enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName));
}
Can we change its status programmatically?
I'm trying to make a new PhoneAccount to use my implementation of ConnectionService. In the documentation it says I need to register a new PhoneAccount with TelecomManager and then select it in my phone-app's settings.
Here's my code:
TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
ComponentName componentName = newComponentName("se.example.connectionservicestandalonetest", "se.example.connectionservicestandalonetest.MyConnectionService");
PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(componentName, "Admin");
PhoneAccount phoneAccount = PhoneAccount.builder(phoneAccountHandle, "Admin").build();
telecomManager.registerPhoneAccount(phoneAccount);
As you can see, it creates a new ComponentName that points towards my implementation of ConnectionService, then creates a new PhoneAccountHandle where I supply the ComponentName and a unique account-name. I then supply the PhoneAccountHandle in the PhoneAccount buildes, as well as label (a name?), to create a new PhoneAccount. Lastly I register the account in the telecomManager.
When I open up the phone app, nothing has changed. I see no where I could possibly change the PhoneAccount... Any ideas?
Thanks!
I've got some information that I'll just leave here for posterity.
When building your PhoneAccount, you must add CAPABILITY_CALL_PROVIDER if you make and receive calls on your own, or CAPABILITY_CONNECTION_MANAGER if you want to make or receive calls using the builtin PhoneAccount. Without either, you won't show up in the UI.
As far as I can tell, there is no dedicated API for checking whether the user has enabled your PhoneAccount. However, you can use TelecomManager.addNewIncomingCall for this purpose. Simply provide a Bundle containing a boolean extra (named whatever you want) and set that boolean to true if you're really receiving a call or false if you just want to do a permission check (or vice-versa). Then your implementation of ConnectionService.onCreateIncomingConnection can check your extra and return Connection.createCanceledConnection if you're just doing a permission check. This does not register as a call in the call log, and the ringtone never plays. addNewIncomingCall will throw if your PhoneAccount is not enabled, and succeed if it is.
As noted in the comments above, you can prompt the user to enable your PhoneAccount using TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS. Because the user can enable or disable your PhoneAccount at any time, all operations that require an enabled PhoneAccount (like addNewIncomingCall) should be placed in a try block.
Here is a little more info that might be helpful to others. After you have configured your phone account, the user needs to enable permission for your app. Getting the user to that screen should be easier. I've only seen the TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS action, but it doesn't take you to the proper screen to enable the permission. You have to select "All calling accounts" after launching that activity.
If you would like to take the user directly to the "Calling accounts" screen, I've found that this Intent will take you there.
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.server.telecom","com.android.server.telecom.settings.EnableAccountPreferenceActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
I've tested this with a Motorola G5S Plus and it should also work with mostly stock devices like Nexus and Pixel devices. I'm not sure if it will work with Samsung devices.
As an addendum to j__m's answer: I found a way to check if the phone account is activated without setting up a call:
private boolean checkAccountConnection(Context context) {
boolean isConnected = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
final List<PhoneAccountHandle> enabledAccounts = telecomManager.getCallCapablePhoneAccounts();
for (PhoneAccountHandle account : enabledAccounts) {
if (account.getComponentName().getClassName().equals(MyConnectionService.class.getCanonicalName())) {
isConnected = true;
break;
}
}
}
}
return isConnected;
}
As stated in the Javadoc to android.telecom.TelecomManager.getCallCapablePhoneAccounts()
Returns a list of {#link PhoneAccountHandle}s which can be used to make and receive phone calls. The returned list includes only those accounts which have been explicitly enabled by the user.
In my device i already set a password of device, now i install my app which is managed by device policy manager. now when i call this method
int currentPolicy = devicePolicyManager.getPasswordQuality(demoDeviceAdmin);
if(currentPolicy==262144)passwordType="Alphabetic";
else if(currentPolicy==327680)passwordType="Alphanumeric";
else if(currentPolicy==131072)passwordType="Numeric";
//if(currentPolicy==196608)passwordType="PASSWORD_QUALITY_NUMERIC_COMPLEX";
else if(currentPolicy==393216)passwordType="Complex";
else if(currentPolicy==196608)passwordType="Pattern";
else if(currentPolicy==0)passwordType="None";
it gives me password type none. Now if i set password through device policy manager in my application like this
Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Then now if again get password quality it gives me correct value.
in First time i think device policy manager don't have or store old password type.
So my question is how to get password quality before setting password from my application through device policy manager.
Thank You
getPasswordQuality doesn't report the quality on the current password, it only reports the policy setting of lowest allowed password quality.
Basically getPasswordQuality returns the value set using the setPasswordQuality of the admin or the aggregate value if several admins are active.
The admin can check if the current password is good enough by calling DevicePolicyManager.isActivePasswordSufficient().
Keyguard reports back the current password status to DevicePolicyManager using the hidden method setActivePasswordState.
To my knowledge, this info is not available to a third party admin app.
If an incorrect password is entered, keyguard calls DevicePolicyManager.reportFailedPasswordAttempt which initiates a factory reset if too many failed attempts have been made.
Hope this helps.
/Marek Pola (Employee of Sony Mobile)
For devices before Android 6 (exclusive), you can get the unlocking method type by calling a hidden function getActivePasswordQuality() in class LockPatternUtils (you can call the function via Java reflection). But for Android 6 and above, I have not figured out how to get the unlocking method type.
/**
* Determine authentication method type (DAS, PIN, Password or Biometric)
*/
private String getAuthenticationMethodType(){
String LOCK_PATTERN_UTILS="com.android.internal.widget.LockPatternUtils";
String ACTIVE_PASSWORD_QUALITY="getActivePasswordQuality";
int lockProtectionLevel=0;
try{
Class <?> lockPatternUtilsClass=Class.forName(LOCK_PATTERN_UTILS);
Object lockPatternUtils=lockPatternUtilsClass.getConstructor(Context.class).newInstance(this);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
// Have not figured out how to get the unlocking method type for Android 6 and above devices. The same method will not work on Android 6. See detailed explanation below.
}else {
Method method = lockPatternUtilsClass.getMethod(ACTIVE_PASSWORD_QUALITY);
lockProtectionLevel=Integer.valueOf(String.valueOf(method.invoke(lockPatternUtils,null)));
}
}catch (Exception e){
e.printStackTrace();
}
switch (lockProtectionLevel){
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
return "PIN";
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
return "Password";
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
return "DAS";
case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK:
return "Biometric";
default:return "None";
}
}
For Android 6, after all my tests so far, I found that if we still want to get the unlocking method with function getActivePasswordQuality(int userId), the app must have permission android.permission.ACCESS_KEYGUARD_SECURE_STORAGE in order to read lockscreen.password_type, and get the unlocking method. However, the permission ACCESS_KEYGUARD_SECURE_STORAGE is a signature permission, which means we need to sign the app with the same key that the manufacturers use to sign the system (or to say the permission), so as to grant the signature permission to the app successfully.
Hope this helps.
I am using AccessibilityService to monitor notifications. I followed this and this. Finally, it works and I get to know about new notifications.
But, to Connect the AccessibilityService I need to ask user to enable it from the ACCESSIBILITY_SETTINGS, which I do this way:
Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivityForResult(intent, 0);
But, the problem is I want to check if the user has enabled it (AccessibilityService) or not. Also, I don't what to ask users again to enable it if they have already enabled it. So, is it possible to do this?
You can test whether the user has enabled your Accessibility Service by checking the secure settings. Settings.Secure.getString() will give you a : separated list of enabled services, and you can check whether yours is in there. Something like this:
ComponentName compName = new ComponentName(context, MyAccessibilityService.class);
String flatName = compName.flattenToString();
String enabledList = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
boolean isEnabled = enabledList != null && enabledList.contains(flatName);
How can I check for GPS support in-App to add a feature for those with Location services enabled?
My concern is, I know I'd have to specify the tag in the manifest to declare that the app uses location services, but I still want the app to function for those without. I just want to check and, if the service is available, use it; otherwise just ignore that one feature.
Thanks.
You can check if any Location services are enabled by looking to see if any location providers are enabled. I'm using this function in my code right now:
public static boolean areProvidersEnabled(Context context) {
ContentResolver cr = context.getContentResolver();
String providersAllowed = Settings.Secure.getString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return providersAllowed != null && providersAllowed.length() > 0;
}
Another neat thing is that you can send the user straight to the Location settings and ask them if they want to enable it. I'll leave it to you on how to ask the user, but the Intent to get to the settings is like this:
Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
startActivity(intent);