Media controls notification emits alert on Android O - android

Ever since adding support for Android O, Android devices running O receive an alert (tone or vibration) whenever my app's media controls notification gets updated (metadata or playback state changes). I'm looking for a way to disable this alert since it's not applicable to media style notifications.
Here is the code I use to create a media controls notification:
MediaDescriptionCompat description = metadata.getDescription();
String artistName = metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
String albumName = metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM);
Bitmap largeIcon = BitmapFactory.decodeResource(playbackService.getResources(),
R.drawable.vector_global_defaultsong);
NotificationCompat.Builder notificationBuilder = new NotificationCompat
.Builder(playbackService, NotificationHelper.CHANNEL_MEDIA_CONTROLS)
.setColor(ContextCompat.getColor(playbackService, R.color.colorAccent))
.setSmallIcon(R.drawable.logo_light_filled)
.setContentTitle(description.getTitle())
.setContentText(playbackService.getString(R.string.song_list_subtitle_format,
artistName, albumName))
.setContentIntent(createContentIntent())
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(playbackService,
PlaybackStateCompat.ACTION_STOP))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOngoing(playbackState.getState() == PlaybackStateCompat.STATE_PLAYING)
.setLargeIcon(largeIcon);
notificationBuilder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
// show previous, play/pause, and next in compact view
.setShowActionsInCompactView(addActions(notificationBuilder))
.setMediaSession(sessionToken));
showNotification(notificationBuilder.build());
. . .
private void showNotification(final Notification notification) {
if (!started) {
mediaController.registerCallback(mediaControllerCallback);
playbackService.startForeground(NOTIFICATION_ID, notification);
started = true;
} else {
notificationManager.notify(NOTIFICATION_ID, notification);
}
if (playbackState.getState() == PlaybackStateCompat.STATE_PAUSED) {
playbackService.stopForeground(false);
}
}
Here is the code I use to create the notification channel:
public static final String CHANNEL_MEDIA_CONTROLS = "media_controls";
public static void createNotificationChannels(Context context) {
if (android.os.Build.VERSION.SDK_INT >= 26) {
NotificationChannel mediaControlsChannel = new NotificationChannel(CHANNEL_MEDIA_CONTROLS,
context.getString(R.string.notification_channel_media_controls),
NotificationManager.IMPORTANCE_HIGH);
mediaControlsChannel.setShowBadge(false);
getNotificationManager(context).createNotificationChannel(mediaControlsChannel);
}
}
Update: Setting the showBadge to false on the notification channel doesn't appear to do anything. I still receive a badge on the app icon when the media controls notification is shown. So it looks like the notification channel attributes that I set are not being applied.

Per the Migrating MediaStyle notifications to Android O, you should be using IMPORTANCE_LOW, which does not contain any sound - IMPORTANCE_HIGH channels have sound associated with them.
Android already reorders MediaStyle notifications higher in the tray, so using a higher importance is not necessary as it was on previous versions of Android.
NOTE: After changing the importance, you need to clear app data or reinstall the app in order to have this change take effect in notification channel.

Related

Custom Android notification sound

I'm trying to implement a custom notification sound in my application.
I have written the following code, but the application plays only default sound and not the custom sound i've added in raw folder. Upon receiving the notification, the logs doesn't even throw any error or exception as to why it isn't playing the custom sound. I tried searching online and tried following different approaches but to no avail.
Please let me know where am i going wrong.
Edit: Can someone post the code for it, i cant seem to find anything that works
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.notify);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("MyCuS Notification", "My Notification", NotificationManager.IMPORTANCE_HIGH);
NotificationManager manager = getSystemService(NotificationManager.class);
AudioAttributes.Builder audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
channel.setSound(Uri.parse("android.resources://" + getPackageName() + "/" + R.raw.bg_reminder_alarm),audioAttributes.build());
manager.createNotificationChannel(channel);
}
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, "MyCuS Notification");
builder.setContentTitle("MyTitle");
builder.setContentText("TESTING");
builder.setSmallIcon(R.drawable.ic_launcher_background);
builder.setAutoCancel(true);
builder.setSound(Uri.parse("android.resources://" + getPackageName() + "/" + R.raw.bg_reminder_alarm));
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(MainActivity.this);
managerCompat.notify(1, builder.build());
}
});
}
Edit 2: I tried deleting existing channel and sending notification to create new channel, when newly created the description of the channel changes after sending second notification, it is as if the channel is overridden or deleted and new default channel is created.
Since Android Oreo / 8 the Notificationsound is coming from the Channel and can only be set the first time you add the channel via your channel.setSound().
If you want to change it later on you need to delete the channel and then re-add it to the system. The user will be warned about that behaviour though (App deleted channels X amount of times).
https://developer.android.com/guide/topics/ui/notifiers/notifications#ManageChannels
If you want to have a customsound each and every time, you need a ForegroundService without a channelsound for it's foreground notification (setSound(null)) and then use the MediaPlayer on the Notificationstream to play the custom sound.

Modify sound of incoming Notification in Android

I have set up a NotificationListenerService which listens for notifications. I need to modify the sound/alert tone of the notifications which is caught in the method below :
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
// Modify the tone here and notify ( the notification ) it again
}
What I have tried so far ( does not play the sound ):
notification.notification.defaults = android.app.Notification.DEFAULT_VIBRATE
notification.notification.sound = Uri.parse(sharedPreferences.getString(getString( R.string.ringtone_key ) , Settings.System.DEFAULT_NOTIFICATION_URI.toString() ))
manager.notify( RECREATE_NOTIFICATION_ID , notification.notification )
My question goes here:
How can I modify the sound/alert tone of the StatusBarNotification caught in the above method and display it to the user? Do I need to repost/recreate it again?
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
{
if (soundUri != null)
{
// Changing Default mode of notification
notificationBuilder.setDefaults(Notification.DEFAULT_VIBRATE)
// Creating an Audio Attribute
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
// Creating Channel
val notificationChannel = NotificationChannel("CH_ID", "Testing_Audio", NotificationManager.IMPORTANCE_HIGH)
notificationChannel.setSound(soundUri, audioAttributes)
mNotificationManager.createNotificationChannel(notificationChannel)
}
}
To add some context to ismail alaoui's answer - what you did should probably work for pre-Oreo android devices, but for Oreo and above, you need to create a notification channel, to which the custom sound will be assigned. Refer to https://developer.android.com/guide/topics/ui/notifiers/notifications.
Please also remember, that user might change the sound of the notification channel at any moment :)
So only question remaining - which Android version are you testing your solution on?

Why Firebase Function calling Firebase admin.messaging.sendToDevice() cannot create notification on Android device

I have an Android app, and am trying to send a notification to a specific device on which it is installed, using the Firebase Messaging API from a Firebase Function. But the notification never shows up. However, sending manually from the Firebase Console does make a notification show up successfully on the same device. Here is the relevant Firebase Function code:
var message = {
notification: {
title: notifTitle,
body: notifBody,
sound: 'default',
android_channel_id: 'update_channel',
channel_id: 'update_channel'
// tag:
}
};
var options = {}
console.log(message);
console.log('registrationToken: ' + registrationToken);
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().sendToDevice([registrationToken], message, options)
.then((response) => {
console.log(response);
...
Which logs the following when invoked, appearing to have sent the notification successfully:
{ results: [ { messageId: '0:1544572985549904%5be491645be49164' } ],
canonicalRegistrationTokenCount: 0,
failureCount: 0,
successCount: 1,
multicastId: 5825519571250606000 }
In my Android app I've created the update_channel in the main activity's onCreate() (because, from what I've gathered, Android Oreo API 26+ requires a specific channel...):
private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String id = getString(R.string.update_notification_channel);
CharSequence name = getString(R.string.app_name);
String description = getString(R.string.app_name);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(id, name, importance);
channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
//channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
And also setting it as the default channel in the Manifest:
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="update_notification_channel"/>
I don't know if the problem is channel-related, or if I'm missing a step somewhere, or what.
For notifications to work while the app is in the foreground or background, you must implement and register a FirebaseMessagingService in your Android app, to receive calls on onMessageReceived(), then create notifications with the NotificationManager in there manually when a notification message comes in.
From the documentation:
Firebase notifications behave differently depending on the foreground/background state of the receiving app. If you want foregrounded apps to receive notification messages or data messages, you’ll need to write code to handle the onMessageReceived callback.

Bundled notification replaces the first notification

When creating a bundled notification using setGroup() and setGroupSummary() I am having some strange issues regarding the behaviour of the notifications.
So, as a reference. This example contains the issue:
var isFirstNotificationInGroup = true
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager.activeNotifications.forEach {
if (it.notification.group == groupId) {
isFirstNotificationInGroup = false
}
}
}
val builder = NotificationCompat.Builder(this, channelId).apply {
color = resources.getColor(R.color.colorAccent)
priority = NotificationCompat.PRIORITY_MAX
setSmallIcon(R.drawable.ic_dotoo_logo)
setContentTitle(title)
setContentText(body)
setStyle(NotificationCompat.BigTextStyle()
.bigText(body))
setAutoCancel(true)
setCategory(NotificationCompat.CATEGORY_SOCIAL)
setGroup(groupId)
setGroupSummary(isFirstNotificationInGroup)
}
< ... >
with(NotificationManagerCompat.from(this)) {
notify(notificationId, builder.build())
}
What happens?
The first notification will be shown as it should. So no issues here.
Then, when we show the second notification. It replaces the first one. This shouldn't happen. And no, it is not due to the notification ID. That's not related to this as far as I know.
But, when we show a third (or more) notification, the bundle works as expected and shows two (or more) bundled notifications. But the first one is... gone.
Thanks in advance for helping me.
I have fixed it by creating a seperate summary notification when isFirstNotificationInGroup is true.
This will be send just before the 'real' notification will be send.

Android - Remove action button from notification

I want to dismiss the notification action buttons (not the whole notification) when clicking on those action buttons. (Lets say: a download notification with stop action button. When click on stop, dismiss stop button and change contentText to 'Download canceled')
The only thing it comes to my mind is to cancel notification and notify another one with the same id, but this seems to be an ugly workaround...
So, is there any way to remove action buttons from notifications?
(i think there is no need to put any code, but I will if its necessary...)
If you are using the NotificationCompat.Builder from the v4 Support Library, you can simply access the builder's action collection directly (Unfortunately no public mutators are provided).
The following will do the trick (Of course you must update re-notify):
NotificationCompat.Builder notifBuilder = NotificationCompat.Builder(context);
...
notifBuilder.mActions.clear();
I am using following workaround:
NotificationCompat.Builder builder = //existing instance of builder
//...
try {
//Use reflection clean up old actions
Field f = builder.getClass().getDeclaredField("mActions");
f.setAccessible(true);
f.set(builder, new ArrayList<NotificationCompat.Action>());
} catch (NoSuchFieldException e) {
// no field
} catch (IllegalAccessException e) {
// wrong types
}
from here: https://code.google.com/p/android/issues/detail?id=68063
Note:
Proguard may break the button clearing in obfuscated build. Fix is to add the following two lines in proguard-rules.pro
-keep class androidx.core.app.NotificationCompat { *; }
-keep class androidx.core.app.NotificationCompat$* { *; }
I had the same problem and found a solution for this.
I created another builder and added two "empty" actions like this:
builder.addAction(0, null, null);
builder.addAction(0, null, null);
(one for each button I had, so if you have three, call it three times).
Then when calling Notify, it removes the buttons.
Even though the accepted answer works, as per documentation, the designed way to do this is by using NotificationCompat.Extender class. For example in Kotlin:
private val clearActionsNotificationExtender = NotificationCompat.Extender { builder ->
builder.mActions.clear()
builder
}
private val notificationBuilder by lazy {
NotificationCompat.Builder(context)
.addAction(R.drawable.ic_play_arrow, "Play", playPendingIntent)
}
private fun updateNotification(){
notificationBuilder
.extend(clearActionsNotificationExtender) // this will remove the play action
.addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)
}
NotificationCompat.Builder notifBuilder = NotificationCompat.Builder(context);
remove Whole Action Button :
builder.mActions.clear();
for remove special action button :
builder.mActions.remove(index);
finally :
notificationManager.notify(notificationID, builder.build());
Android provides the notification area for alerting users about the events that have occurred. It also provides a notification drawer that user can pull down to see more detailed information about the notification.
Notification Drawer consists of
View (contains tittle,detail,small icon)
Action ( any action which may occur in case the user clicks the notification drawer view)
To set up a notification so it can be updated, issue it with a notification ID by calling NotificationManager.notify(ID, notification). To update this notification once you've issued it, update or create a NotificationCompat.Builder object, build a Notification object from it, and issue the Notification with the same ID you used previously.
mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Sets an ID for the notification, so it can be updated
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("New Message")
.setContentText("You are downloading some image.")
.setSmallIcon(R.drawable.ic_stop)
numMessages = 0;
// Start of a loop that processes data and then notifies the user
...
mNotifyBuilder.setContentText("Download canceled")
.setNumber(++numMessages);
// Because the ID remains unchanged, the existing notification is
// updated.
mNotificationManager.notify(
notifyID,
mNotifyBuilder.build());
...

Categories

Resources