I'm facing unwelcome behavior when asking new POST_NOTIFICATIONS permission on some particular device owned by remote tester - Pixel 4a (Android 13). Sadly don't have logs and "cable access". Freshly installed app - still targetting API30, but also tried with target set to 33, just for test - should show custom splashscreen Activity, after that shows "main" Activity, which in onResume tries to create NotificationChannel. This should cause perm dialog pop up
If your app targets 12L (API level 32) or lower, the system shows the permission dialog the first time your app starts an activity after you create a notification channel, or when your app starts an activity and then creates its first notification channel. This is usually on app startup.
Well, not on this Pixel 4a with Android 13, meanwhile on Pixel 6 with Android 13 dialog shows up...
Funniest thing is... When tester install app, runs first time, no dialog, then kill it, navigate to system settings and clear data/cache (or even won't make first run, just clean after installation), then dialogs shows up at "first" run...
Why?!
Edit: so now I can reproduce problem also on Pixel 6. I've introduced middle-Dialog with info about content in pushes/notifications and simple yes/no buttons. "Yes" is creating (first) NotificationChannel and this doesn't cause POST_NOTIFICATIONS perm dialog appearance...
#RequiresApi(Build.VERSION_CODES.O)
fun addStaticNotificationChannel(channelId: String, nameResId: Int, descriptionResId: Int,
importance: Int, soundOn: Boolean = true, forceRecreate: Boolean = false): String {
val name = context.resources.getText(nameResId).toString()
val description = context.resources.getText(descriptionResId).toString()
/*if (manager.getNotificationChannel(channelId) != null) {
if (forceRecreate) manager.deleteNotificationChannel(channelId)
else return channelId
}*/
val channel = NotificationChannel(channelId, name, importance)
channel.description = description
channel.lockscreenVisibility = VISIBILITY_PUBLIC
channel.setShowBadge(true)
channel.enableLights(true)
channel.lightColor = ContextCompat.getColor(context, R.color.tsi_blue)
if (!soundOn)
channel.setSound(null, null)
Log.i(this.javaClass.simpleName, "createNotificationChannel channeldId:$channelId")
manager.createNotificationChannel(channel)
return channelId
}
I've messed up targetSdk and branches of my project, and turned out that:
asking perm straight (ContextCompat.request...) when targetting < 33 won't show any dialog
creating channel when targetting 33+ and no permission granted (yet?) also won't show any dialog
so comprehensive future-proof code for those, who can't jump to 33 right away would be
val targetSdkVersion: Int = activity.applicationContext.applicationInfo.targetSdkVersion
if (targetSdkVersion < 33) {
// behavior when targeting < 33 (Android 13), first channel creation will show perm prompt
Log.i(this.javaClass.simpleName, "trying to create channel, when no perm")
addStaticNotificationChannel(SOME_CHANNEL_ID, R.string.notification_some_channel_title,
R.string.notification_some_channel_desc, IMPORTANCE_LOW, forceRecreate = true)
} else {
Log.i(this.javaClass.simpleName, "asking for notification perm")
ActivityCompat.requestPermissions(activity, arrayOf(android.Manifest.permission.POST_NOTIFICATIONS), 89)
}
Related
I updated targetSdkVersion from 30 to 33 and notifications popup is not shown when the app is installed on the device
when the targetSdkVersion is 30 and when I install the app, the following popup shows up and when I click allow I do get notifications
when the targetSdkVersion is 33, I donot get the following popup when the app is installed.
I looked at https://developer.android.com/develop/ui/views/notifications/notification-permission
and added <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> in manifest file.
still I do not get any prompt asking for permission for notifications.
I have code which checks if notification is enabled and it returns null cuz no prompt is shown.
private fun isNotificationsEnabled(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val manager = oApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (!manager.areNotificationsEnabled()) {
return false
}
val channels = manager.notificationChannels
for (channel in channels) {
if (channel.importance == NotificationManager.IMPORTANCE_NONE) {
return false
}
}
true
} else {
NotificationManagerCompat.from(oApp).areNotificationsEnabled()
}
}
How can i force prompt or what else I need to do so user get this prompt on first install
thanks in advance
R
As shown in the documentation here, the new POST_NOTIFICATIONS permission is classified as a dangerous permission, which means you must manually request the permission from the user at runtime. The process for requesting a dangerous permission from the user is explained in the documentation here. Best practices for dangerous permissions state that you should not request the permission until the point when the user requests the behavior that requires the permission.
Prior to Android 12, I used code below to detect physical volume buttons pressed to show my custom UI, but it stopped working on Android 12 devices and onAdjustVolume is never called when I press volume buttons:
mediaSessionCompat = MediaSessionCompat(context, "My App")
mediaSessionCompat?.isActive = true
mediaSessionCompat?.setPlaybackState(
PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1f)
.build())
mediaSessionCompat?.setPlaybackToRemote(object :
VolumeProviderCompat(VOLUME_CONTROL_ABSOLUTE, 7, 4) {
override fun onAdjustVolume(direction: Int) {
Log.v("My App", "Volume adjusted")
}
})
Please, check this link:
https://issuetracker.google.com/issues/201546605?pli=1
It seems like Google changed it because of "privacy changes", according to them, but it is supposed it will be reverted in the next release (nobody knows when).
Again about notification sound on Android O+.
There are some phones where the "notification settings" window doesn't show the sound selection button (and not even the vibration button).
Here are a couple examples:
Samsung A5
Huawei Honor View 10
(not minor brands... I would say)
They were tested with Gmail app (menu -> Settings -> account -> Notification settings) on Android 8.
Here Android O - Notification Channels - Change Vibration Pattern or Sound Type is a solution to avoid the "standard" window, but why should we reinvent the wheel?
Is there any other option that I'm missing?
Thank you,
Max
P.S.
Here is a screenshot from a Honor 9 / Android 8.0.0.
Channel name is "Mail" ("Posta" in Italian). For sound ("Suoneria" in Italian) there is only an On/Off switch.
It's a mess. You need to add workarounds for the different brands/devices. This is the flow we're using to deal with it:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isDeviceWithWorkaround()) {
// Send to notification channel settings (See https://developer.android.com/training/notify-user/channels#UpdateChannel)
}else{
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Sound");
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(someExistingRingTone));
if (isDeviceWithWorkaround()) {
intent.setPackage("com.android.providers.media");
}
try {
startActivityForResult(intent, reqCode);
} catch (ActivityNotFoundException e) {
if (isDeviceWithWorkaround()) {
Log.i(TAG, "Failed to find preferred package [" + intent.getPackage() + "]. Trying again without package.");
intent.setPackage(null);
startActivity(intent);
}
}
}
So what's happening is that if it's a device with a known issue as you talk about we send them to the good old ringtone picker.
I believe that the package com.android.providers.media doesn't have an activity to start on stock Android, but on Huawei it then opens the Media Store where we get back a ringtone URI that can be used as notification sound. (We don't want the user to end up in some other ringtone picker that might not work. We have always recommended our users to use https://play.google.com/store/apps/details?id=com.angryredplanet.android.rings_extended but it won't work with Huawei on Android 8).
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;
}
I was just wondering if there's a way to check if an Android device (21+) is in Do Not Disturb mode? I know there's AudioManager.RINGER_MODE_SILENT, but I was wondering if that pertains to this situation, or if there's a better way to check?
With pointers from Android how to turn on do not disturb (dnd) programmatically:
In SDK 23, android.app.NotificationManager provides the interface you need, namely NotificationManager.getCurrentInterruptionFilter().
It should return one of:
INTERRUPTION_FILTER_PRIORITY
INTERRUPTION_FILTER_ALARMS
INTERRUPTION_FILTER_NONE
INTERRUPTION_FILTER_ALL
INTERRUPTION_FILTER_UNKNOWN
According to Google Help on Nexus Devices Do not Disturb is a feature of Android >= 6.0, so SDK 23 should be reasonable to ask. If it is not, I think it would be reasonable to ask why, to be able to provide a workaround.
I'm bringing the necro with this post, but was just searching for a solution to the same problem and came past this post as I did, so am leaving a solution for future ref.
I couldn't find an "official" way to do this, but did find a value that can return the DnD state as an integer. The feature is called "zen_mode" internally, so you can check the current value with the following code:
Global.getInt(getContentResolver(), "zen_mode")
That'll return:
0 - If DnD is off.
1 - If DnD is on - Priority Only
2 - If DnD is on - Total Silence
3 - If DnD is on - Alarms Only
When the setting is priority only, it returns nothing, but you can figure out that state by assuming no news = Priority Messages mode. This must have been a bug, as now it's a year on, this now returns a value just like the other states. No idea when it was fixed, but it now works as you'd expect.
I tried with a settings observer too, which works but only for certain state transitions, so I think polling periodically is the best bet, until an "official" way to listen for this state change becomes available.
I've included both observer and polling methods of getting the value in this Gist. Hopefully it'll help/save someone else some time.
Update 15th March 2017: Thanks to David Riha for the comment. Added 1 - Priority Only state to answer, revised the Gist to recognise that state, and to output unknown values to log in case any other devices or future updates decide to return a different value.
You need to first add to the manifest file, permission to change the audio settings. To be able to set ringer mode to silent, you must ask permission to access notification policy
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
and then to detect the "do not disturb" you can do as follows
NotificationManager notificationManager = (NotificationManager) getActivity().getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
// Check if the notification policy access has been granted for the app.
if (!notificationManager.isNotificationPolicyAccessGranted()) {
Intent intent = new
Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);
return;
}
ToggleDoNotDisturb(notificationManager);
private void ToggleDoNotDisturb(NotificationManager notificationManager) {
if (notificationManager.getCurrentInterruptionFilter() == NotificationManager.INTERRUPTION_FILTER_ALL) {
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
audioToggle.setText(R.string.fa_volume_mute);
} else {
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
audioToggle.setText(R.string.fa_volume_up);
}
}
also you need to check permissions
NotificationManager mNotificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
// Check if the notification policy access has been granted for the app.
if (!mNotificationManager.isNotificationPolicyAccessGranted()) {
Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);
}
NotificationManager.Policy a = null;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
a = mNotificationManager.getNotificationPolicy();
Log.d(TAG, "onClickDND: "+a.priorityCallSenders);
Log.d(TAG, "onClickDND: "+a.priorityCategories);
}
output
--------------------------------------
FROM ANYONE
call senders : 0
categories : 109
Message : 0
State : 1
FROM CONTACTS ONLY
call senders : 1
categories : 109
Message : 1
State : 1
FROM STARRED CONTACTS ONLY
call senders : 2
categories : 109
Message : 2
State : 1
NONE
call senders : 2
categories : 97
Message : 2
State : 1