With Android O we get the "Notification Channels".
As far as I understand that means that the user can't set the notification tone or other related Notification settings inside the APP anymore .
The user needs to go to the "Notification Channels Setting" and change the tone or vibration etc. here because all methods from the NotificationBuilder like setSound are getting ignored.
So there is really NO way to change the tone to silent via code?
Or to change the vibration pattern via code?
For example the user have the ability to set the vibration pattern in my app.
Or he can pick tones from the alarm type instead of the notification type.
All this is not possible anymore?
Is this right or is there any way to do this?
What Are the Benefits of Using Notification Channels? By adding notification channels to Android, Google allowed end users to control their receipt of push messages, so that they can be alerted to the types of messages they want most, while opting out of the ones that don't interest them.
What Are Notification Channels? Notification channels enable us app developers to group our notifications into groups—channels—with the user having the ability to modify notification settings for the entire channel at once.
You can still offer sound and vibration customization in your app, but it requires a different approach. In short, the idea is to play sound and vibration manually in Android O instead of using the notification channel (it's easier than it seems).
This is how I did it:
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId); // builder.setSmallIcon(...) // builder.setContentTitle(...) // builder.setContentText(...) if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // play vibration vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(VibrationEffect.createWaveform(vibrationPattern, -1)); // play sound Intent serviceIntent = new Intent(context, SoundService.class); serviceIntent.setAction("ACTION_START_PLAYBACK"); serviceIntent.putExtra("SOUND_URI", soundUri.toString()); context.startForegroundService(serviceIntent); // the delete intent will stop the sound when the notification is cleared Intent deleteIntent = new Intent(context, SoundService.class); deleteIntent.setAction("ACTION_STOP_PLAYBACK"); PendingIntent pendingDeleteIntent = PendingIntent.getService(context, 0, deleteIntent, 0); builder.setDeleteIntent(pendingDeleteIntent); } else { builder.setVibrate(vibrationPattern); builder.setSound(soundUri); } notificationManager.notify(notificationId, builder.build());
SoundService.class is the place where I play the sound with MediaPlayer:
public class SoundService extends Service { MediaPlayer mMediaPlayer; @Override public IBinder onBind(Intent intent) { return null; } public int onStartCommand(Intent intent, int flags, int startId) { // foreground notification if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationCompat.Builder builder = new NotificationCompat.Builder(this, otherChannelId); builder.setSmallIcon(...) .setContentTitle(...) .setContentText(...) .setAutoCancel(true); startForeground(foregroundNotificationId, builder.build()); } // check action String action = intent.getAction(); switch (action) { case "ACTION_START_PLAYBACK": startSound(intent.getStringExtra("SOUND_URI")); break; case "ACTION_STOP_PLAYBACK": stopSound(); break; } // service will not be recreated if abnormally terminated return START_NOT_STICKY; } private void startSound(String uriString) { // parse sound Uri soundUri; try { soundUri = Uri.parse(uriString); } catch (Exception e) { cleanup(); return; } // play sound if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mediaPlayer) { cleanup(); } }); } try { mMediaPlayer.setDataSource(this, soundUri); mMediaPlayer.prepareAsync(); } catch (Exception e) { cleanup(); } } private void stopSound() { if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; } cleanup(); } private void cleanup() { stopSelf(); } }
Recommendations
setSound(null,null)
) and a null vibration (setVibrationPattern(null)
) and explain in the channel description that this is the recommended setting in order to avoid conflicts with the app's own customization.Foreground notification
Starting Android O, services started from the background must be started as foreground services. This means SoundService needs a foreground notification. You have some options for this:
Create a nice foreground notification with a button that says 'Stop playback' so that the user can stop the sound without removing the notification that started it.
Create a simple notification and send it to a disabled channel (you can create disabled channels if you create them with IMPORTANCE_NONE). Doing this, the default system 'App is running in the background' notification will appear instead of your foreground notification, but users can hide this notification from the status bar if they want.
EDIT: in Android 8.1 it seems that creating a disabled channel with IMPORTANCE_NONE is not useful, as the channel will be enabled automatically when you send a notification. It may be better to create it with IMPORTANCE_LOW from the beginning and let users change the importance if they want.
Might help in your case. If you have one notification displayed you can disable sound when updating this notification by setting .setOnlyAlertOnce(true)
. This solution works only when updating notification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With