How to check whether my SMS app is default in Android - android

I want to check whether my SMS app is set as default app in android. I am following this tutorial:
http://android-developers.blogspot.com/2013/10/getting-your-sms-apps-ready-for-kitkat.html
I can set my SMS app as default SMS app by the following code:
Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, context.getPackageName());
startActivity(intent);
But I want to check whether my SMS app is set as default app. How can I do that?

You can use getDefaultSmsPackage (Context context):
Used to determine the currently configured default SMS package.
For example:
public static boolean isDefaultSmsApp(Context context) {
return context.getPackageName().equals(Telephony.Sms.getDefaultSmsPackage(context));
}

Related

Change Default SMS App handler Android Q RoleManger

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
// check if the app is having permission to be as default SMS app
boolean isRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_SMS);
if (isRoleAvailable){
// check whether your app is already holding the default SMS app role.
boolean isRoleHeld = roleManager.isRoleHeld(RoleManager.ROLE_SMS);
if (isRoleHeld){
Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS);
((AppCompatActivity)context).startActivityForResult(roleRequestIntent, ConstantsValues.REQUEST_CODES.REQUEST_RESET_SMS_HANDLER);
}
}
}else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, Custom_SharedPreference.getInstance(context).getDefaultSMSPackage());
((AppCompatActivity)context).startActivityForResult(intent, ConstantsValues.REQUEST_CODES.REQUEST_RESET_SMS_HANDLER);
}
This is working fine when Change SMS App to MyApp default handler but When I change MyApp to again Default SMS App then its not working and activityResult return 0 Activity.Cancle.
This is happend only Android Q other Versions working fine.
In short, it's not possible to revert default SMS app.
As a work-around I decided to open the previous default SMS app using this code:
runIntent = getPackageManager().getLaunchIntentForPackage(newDefaultSmsApp);
Having the same issue, I tried various workaround, like using old method to request a change of default SMS app, however the OS doesn't care at all. Using RoleManager, when requesting to "revert" the role back to default/previous SMS app, the logs shows something like this:
2021-06-18 08:23:06.246 1770-5101/? I/ActivityTaskManager: START u0 {act=android.provider.Telephony.ACTION_CHANGE_DEFAULT cmp=com.google.android.permissioncontroller/com.android.permissioncontroller.role.ui.RequestRoleActivity (has extras)} from uid 10043
2021-06-18 08:23:06.246 1770-5101/? W/PermissionPolicyService: Action Removed: starting Intent { act=android.provider.Telephony.ACTION_CHANGE_DEFAULT cmp=com.google.android.permissioncontroller/com.android.permissioncontroller.role.ui.RequestRoleActivity (has extras) } from ccc71.sb (uid=10043)
Note the Action Removed on second line.
So in summary, it's only possible to request the role for self, but impossible to revert. A real bummer for backup apps that Google is making look bad, like so many other apps these days.

How to access notification channels for another Android app?

I want to create a function that calls the notification channel settings for another app. I don't know the channel IDs for the other app. Is there a way to do that?
You cannot access the notification channels of another app, neither what channels there are nor what their settings are.
The only thing you can do is to open the notification settings overview of another app, given its package name (Facebook for example):
Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, "com.facebook.katana");
startActivity(intent);
This can be done through reflection. Note that more hidden APIs are blocked with every Android release, so this is not a long-term solution.
First, add the android.permission.STATUS_BAR_SERVICE permission to AndroidManifest.xml. My IDE warns that this is a system app permission, but my device (running Xiaomi's MIUI 12.5, Android 11) allows it.
<manifest ...>
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE"
tools:ignore="ProtectedPermissions"/>
...
</manifest>
Now, any app's channels can be accessed:
// Retreive an instance of the INotificationManager service using the
// hidden NotificationManager.getService static method.
val sINM = NotificationManager::class.java.getMethod("getService").invoke(null)
// Find the INotificationManager.getNotificationChannelsForPackage method.
val getNotificationChannelsForPackageMethod =
sINM::class.java.getMethod(
"getNotificationChannelsForPackage",
java.lang.String::class.java,
Integer.TYPE,
java.lang.Boolean.TYPE,
)
// Retreive information about an app.
val applicationInfo = packageManager.getApplicationInfo("com.example", 0)
// Retreive a ParceledListSlice of the app's NotificationChannel objects.
// The boolean is the "includeDeleted" parameter.
val channelsSlice = getNotificationChannelsForPackageMethod.invoke(
sINM,
applicationInfo.packageName,
applicationInfo.uid,
false,
)
// Retreive the channels in the form of an ArrayList<NotificationChannel>.
val channels = channelsSlice::class.java.getMethod("getList")
.invoke(channelsSlice) as ArrayList<NotificationChannel>
AOSP references:
NotificationManager.java
INotificationManager.aidl
BaseParceledListSlice.java
No, You can not handle other app Notification Channel because every change has id and without id System can not get that channel.
According to my knowledge we can not get notification channel ids of other app.
You can access notification channels of other apps using NotificationListenerService, which is in-built in SDK. After registering the service and giving notification access permission (using Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")), it is possible to just call something like the following method:
private List<NotificationChannel> getChannels() {
final var ranking = getCurrentRanking();
final var channelsList = new ArrayList<NotificationChannel>();
for (var notification : getActiveNotifications()) {
final var currentRanking = new Ranking();
ranking.getRanking(notification.getKey(), currentRanking);
channelsList.add(currentRanking.getChannel());
}
return channelsList;
}

DevicePolicyManager.enableSystemApp() method does not work as expected

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.an‌​droid.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.

Android: register new PhoneAccount for telecom

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.

Redirect to Notification Access Settings

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()

Categories

Resources