How to use Custom Notification Sounds on Android?

On Android 8.0 and higher, notification sounds are managed using Notification Channels. Users can reconfigure those channels. You control the default settings (including the sound and importance), but users can change them at any time. The system always applies the user-defined configuration.

Batch automatically registers a default channel when displaying the first notification if no channel override is set. You cannot change the sound of this channel. You must implement your own channel to define a custom sound.

You must declare your notification channels before sending notifications. Register them during your application’s startup phase. Then, configure Batch to use them by overriding the default channel or by implementing a NotificationChannelIdInterceptor.

Change the Default Sound

To change the default notification sound:

  1. Create your own notification channel and register it with Android.

Implement Multiple Sounds Based on the Notification Payload

To implement multiple notification sounds:

  1. Create multiple notification channels (one per sound).

  2. Implement a NotificationChannelIdInterceptor and return the appropriate channel based on the custom payload.

Ensure backwards compatibility

Notification channels are not available on versions earlier than Android 8.0. You must implement a NotificationInterceptor to change the notification sound.

Code example

This code implements multiple notification sounds based on the payload content:

  • A payload of { "channel": "win" } plays sound_win from raw resources.

  • A payload of { "channel": "defeat" } plays sound_defeat from raw resources.

  • Any other value falls back on Batch's default channel.

This snippet works on Android 4.1 and higher. It uses a notification interceptor for devices that do not support channels:


public class MyApplication extends Application {
    public static final String CHANNEL_WIN = "win";
    public static final String CHANNEL_DEFEAT = "defeat";

    public static final String SOUND_RESOURCE_WIN = "sound_win";
    public static final String SOUND_RESOURCE_DEFEAT = "sound_defeat";

    @Override
    public void onCreate() {
        super.onCreate();
        // This code should be in your Application subclass' onCreate, not in your Activity's! It is required so that Batch can call your interceptor while your app is in the background
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            setupNotificationChannels(this);
            setupBatchChannelInterceptor();
        } else {
            setupLegacyBatchSoundInterceptor();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void setupNotificationChannels(@NonNull Context context) {
        // This sample assumes that your sounds are named "sound_win"/"sound_defeat"
        // and placed in the "raw" resource folder.

        AudioAttributes soundAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                .build();

	@SuppressLint("WrongConstant")
        NotificationChannel winChannel = new NotificationChannel(
                CHANNEL_WIN,
                "Win notifications",
                NotificationManager.IMPORTANCE_DEFAULT);
        winChannel.setSound(getUriForSoundName(context, SOUND_RESOURCE_WIN), soundAttributes);

	@SuppressLint("WrongConstant")
        NotificationChannel defeatChannel = new NotificationChannel(
                CHANNEL_DEFEAT,
                "Defeat notifications",
                NotificationManager.IMPORTANCE_DEFAULT);
        defeatChannel.setSound(getUriForSoundName(context, SOUND_RESOURCE_DEFEAT), soundAttributes);

        NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(winChannel);
        notificationManager.createNotificationChannel(defeatChannel);
    }

    @NonNull
    private Uri getUriForSoundName(@NonNull Context context, @NonNull String soundName) {
        return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName()
                + "/raw/" + soundName);
    }

    private void setupBatchChannelInterceptor() {
        Batch.Push.getChannelsManager().setChannelIdInterceptor((payload, deductedChannelId) -> {
            // custom payload: {"channel":"win"}/{"channel":"defeat"}
            String channel = payload.getPushBundle().getString("channel");
            switch (channel) {
                case "win":
                    return CHANNEL_WIN;
                case "defeat":
                    return CHANNEL_DEFEAT;
            }
            return deductedChannelId;
        });
    }

    private void setupLegacyBatchSoundInterceptor() {
        Batch.Push.setNotificationInterceptor(new BatchNotificationInterceptor() {
            @Override
            public NotificationCompat.Builder getPushNotificationCompatBuilder(@NonNull Context context,
                                                                               @NonNull NotificationCompat.Builder defaultBuilder,
                                                                               @NonNull Bundle pushIntentExtras,
                                                                               int notificationId) {
                String sound = pushIntentExtras.getString("channel", null);
                Uri soundUri = null;

                switch (sound) {
                    case "win":
                        soundUri = getUriForSoundName(context, SOUND_RESOURCE_WIN);
                        break;
                    case "defeat":
                        soundUri = getUriForSoundName(context, SOUND_RESOURCE_DEFEAT);
                        break;
                }

                if (soundUri != null) {
                    defaultBuilder.setSound(soundUri);
                }
                return defaultBuilder;
            }
        });
    }
}

Last updated

Was this helpful?