Skip to content

Commit f785400

Browse files
authored
Merge pull request #500 from Iterable/custom-sound
[MOB-5317] Notification channels based on locally hosted custom sound
2 parents b0dc39d + c029ce5 commit f785400

File tree

2 files changed

+88
-58
lines changed

2 files changed

+88
-58
lines changed

iterableapi/src/main/java/com/iterable/iterableapi/IterableConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public final class IterableConstants {
143143
public static final String NOTIFICATION_ICON_NAME = "iterable_notification_icon";
144144
public static final String NOTIFICAION_BADGING = "iterable_notification_badging";
145145
public static final String NOTIFICATION_COLOR = "iterable_notification_color";
146-
public static final String NOTIFICATION_CHANNEL_NAME = "iterable_notification_channel_name";
146+
public static final String NOTIFICATION_CHANNEL_NAME = "Default";
147147
public static final String DEFAULT_SOUND = "default";
148148
public static final String SOUND_FOLDER_IDENTIFIER = "raw";
149149
public static final String ANDROID_RESOURCE_PATH = "android.resource://";

iterableapi/src/main/java/com/iterable/iterableapi/IterableNotificationHelper.java

Lines changed: 87 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
import android.content.pm.ApplicationInfo;
1010
import android.content.pm.PackageManager;
1111
import android.content.res.Resources;
12+
import android.media.AudioAttributes;
1213
import android.net.Uri;
1314
import android.os.Build;
1415
import android.os.Bundle;
16+
import android.provider.Settings;
1517
import android.service.notification.StatusBarNotification;
1618

19+
import androidx.annotation.Nullable;
1720
import androidx.annotation.VisibleForTesting;
1821
import androidx.core.app.NotificationCompat;
1922

@@ -23,7 +26,6 @@
2326
import java.util.Map;
2427

2528
class IterableNotificationHelper {
26-
private static final String DEFAULT_CHANNEL_NAME = "iterable channel";
2729
private static final String NO_BADGE = "_noBadge";
2830

2931
@VisibleForTesting
@@ -101,8 +103,8 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex
101103
String soundName = null;
102104
String messageId = null;
103105
String pushImage = null;
106+
Uri soundUri = null;
104107
//TODO: When backend supports channels, these strings needs to change (channelName, channelId, channelDescription).
105-
String channelName = getChannelName(context);
106108
String channelDescription = "";
107109

108110
if (!extras.containsKey(IterableConstants.ITERABLE_DATA_KEY)) {
@@ -115,13 +117,36 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex
115117
return null;
116118
}
117119

118-
removeUnusedChannel(context);
119-
registerChannelIfEmpty(context, getChannelId(context), channelName, channelDescription);
120-
IterableNotificationBuilder notificationBuilder = new IterableNotificationBuilder(context, getChannelId(context));
121120
JSONObject iterableJson = null;
122121
title = extras.getString(IterableConstants.ITERABLE_DATA_TITLE, applicationName);
123122
notificationBody = extras.getString(IterableConstants.ITERABLE_DATA_BODY);
124123
soundName = extras.getString(IterableConstants.ITERABLE_DATA_SOUND);
124+
String soundUrl = null;
125+
126+
//Check if soundName is a remote sound file
127+
if (soundName != null) {
128+
129+
// If soundname contains remote link, store it as a soundUrl and continue to trim soundName to for channel Id and name
130+
if (soundName.contains("https")) {
131+
soundUrl = soundName;
132+
soundName = soundName.substring(soundName.lastIndexOf('/') + 1);
133+
}
134+
135+
// Remove extension of sound file
136+
soundName = soundName.replaceFirst("[.][^.]+$", "");
137+
}
138+
139+
soundUri = getSoundUri(context, soundName, soundUrl);
140+
141+
String channelName = (soundUri == Settings.System.DEFAULT_NOTIFICATION_URI)
142+
? IterableConstants.NOTIFICATION_CHANNEL_NAME
143+
: getChannelName(soundName);
144+
145+
String channelId = (soundUri == Settings.System.DEFAULT_NOTIFICATION_URI)
146+
? context.getPackageName()
147+
: getCurrentChannelId(context, soundName);
148+
149+
IterableNotificationBuilder notificationBuilder = new IterableNotificationBuilder(context, channelId);
125150

126151
String iterableData = extras.getString(IterableConstants.ITERABLE_DATA_KEY);
127152

@@ -155,23 +180,6 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex
155180
notificationBuilder.setImageUrl(pushImage);
156181
notificationBuilder.setExpandedContent(notificationBody);
157182

158-
if (soundName != null) {
159-
//Removes the file type from the name
160-
String[] soundFile = soundName.split("\\.");
161-
soundName = soundFile[0];
162-
163-
if (!soundName.equalsIgnoreCase(IterableConstants.DEFAULT_SOUND)) {
164-
int soundID = context.getResources().getIdentifier(soundName, IterableConstants.SOUND_FOLDER_IDENTIFIER, context.getPackageName());
165-
Uri soundUri = Uri.parse(IterableConstants.ANDROID_RESOURCE_PATH + context.getPackageName() + "/" + soundID);
166-
notificationBuilder.setSound(soundUri);
167-
} else {
168-
notifPermissions.defaults |= Notification.DEFAULT_SOUND;
169-
}
170-
171-
} else {
172-
notifPermissions.defaults |= Notification.DEFAULT_SOUND;
173-
}
174-
175183
// The notification doesn't cancel properly if requestCode is negative
176184
notificationBuilder.requestCode = Math.abs((int) System.currentTimeMillis());
177185
IterableLogger.d(IterableNotificationBuilder.TAG, "Request code = " + notificationBuilder.requestCode);
@@ -223,6 +231,9 @@ public IterableNotificationBuilder createNotification(Context context, Bundle ex
223231

224232
notificationBuilder.setDefaults(notifPermissions.defaults);
225233

234+
removeUnusedChannel(context, soundName);
235+
registerChannelIfEmpty(context, channelId, channelName, channelDescription, soundUri);
236+
226237
return notificationBuilder;
227238
}
228239

@@ -255,7 +266,7 @@ public void postNotificationOnDevice(Context context, IterableNotificationBuilde
255266
* @param channelName Sets the channel name that is shown to the user.
256267
* @param channelDescription Sets the channel description that is shown to the user.
257268
*/
258-
private void registerChannelIfEmpty(Context context, String channelId, String channelName, String channelDescription) {
269+
private void registerChannelIfEmpty(Context context, String channelId, String channelName, String channelDescription, Uri soundUri) {
259270
NotificationManager mNotificationManager = (NotificationManager)
260271
context.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
261272
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O
@@ -264,21 +275,21 @@ private void registerChannelIfEmpty(Context context, String channelId, String ch
264275
if (existingChannel == null || !existingChannel.getName().equals(channelName)) {
265276
IterableLogger.d(IterableNotificationBuilder.TAG, "Creating notification: channelId = " + channelId + " channelName = "
266277
+ channelName + " channelDescription = " + channelDescription);
267-
mNotificationManager.createNotificationChannel(createNotificationChannel(channelId, channelName, channelDescription, context));
278+
mNotificationManager.createNotificationChannel(createNotificationChannel(channelId, channelName, channelDescription, context, soundUri));
268279
}
269280
}
270281
}
271282

272283
/**
273284
* Safely removes unused and old channel if the configuration for notification badge is changed.
274285
*/
275-
private void removeUnusedChannel(Context context) {
286+
private void removeUnusedChannel(Context context, String soundName) {
276287
NotificationManager mNotificationManager = (NotificationManager)
277288
context.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
278289

279290
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O
280291
&& mNotificationManager != null) {
281-
String channelIdToDelete = getOldChannelId(context);
292+
String channelIdToDelete = getOldChannelId(context, soundName);
282293
NotificationChannel unusedChannel = mNotificationManager.getNotificationChannel(channelIdToDelete);
283294
if (unusedChannel != null) {
284295
for (StatusBarNotification activeNotification : mNotificationManager.getActiveNotifications()) {
@@ -292,14 +303,18 @@ private void removeUnusedChannel(Context context) {
292303
}
293304
}
294305

295-
private NotificationChannel createNotificationChannel(String channelId, String channelName, String channelDescription, Context context) {
306+
private NotificationChannel createNotificationChannel(String channelId, String channelName, String channelDescription, Context context, Uri soundUri) {
296307
NotificationChannel notificationChannel = null;
308+
AudioAttributes audioAttributes = getAudioAttributes();
309+
297310
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
298311
notificationChannel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
299312
notificationChannel.setDescription(channelDescription);
300313
notificationChannel.enableLights(true);
301314
notificationChannel.setShowBadge(isNotificationBadgingEnabled(context));
315+
notificationChannel.setSound(soundUri, audioAttributes);
302316
}
317+
303318
return notificationChannel;
304319
}
305320

@@ -315,16 +330,21 @@ private static boolean isNotificationBadgingEnabled(Context context) {
315330
return true;
316331
}
317332

318-
private String getChannelId(Context context) {
319-
return getChannelIdName(context, true);
333+
private String getCurrentChannelId(Context context, String soundName) {
334+
return getChannelIdName(context, true, soundName);
320335
}
321336

322-
private String getOldChannelId(Context context) {
323-
return getChannelIdName(context, false);
337+
private String getOldChannelId(Context context, String soundName) {
338+
return getChannelIdName(context, false, soundName);
324339
}
325340

326-
private String getChannelIdName(Context context, boolean isActive) {
341+
private String getChannelIdName(Context context, boolean isActive, String soundName) {
327342
String channelId = context.getPackageName();
343+
344+
if (soundName != null) {
345+
channelId = soundName;
346+
}
347+
328348
if (isActive) {
329349
if (!isNotificationBadgingEnabled(context)) {
330350
channelId = channelId + NO_BADGE;
@@ -337,33 +357,14 @@ private String getChannelIdName(Context context, boolean isActive) {
337357
return channelId;
338358
}
339359

340-
private String getChannelName(Context context) {
341-
String channelName = null;
342-
try {
343-
ApplicationInfo info = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
344-
if (info.metaData != null) {
345-
Object channelNameMetaData = info.metaData.get(IterableConstants.NOTIFICATION_CHANNEL_NAME);
346-
if (channelNameMetaData instanceof String) {
347-
// Literal string value
348-
channelName = (String) channelNameMetaData;
349-
} else if (channelNameMetaData instanceof Integer) {
350-
// Try to read from a string resource
351-
int stringId = (Integer) channelNameMetaData;
352-
if (stringId != 0) {
353-
channelName = context.getString(stringId);
354-
}
355-
}
356-
IterableLogger.d(IterableNotificationBuilder.TAG, "channel name: " + channelName);
357-
}
358-
} catch (Exception e) {
359-
IterableLogger.e(IterableNotificationBuilder.TAG, "Error while retrieving channel name", e);
360-
}
360+
private String getChannelName(String soundName) {
361+
String channelName = IterableConstants.NOTIFICATION_CHANNEL_NAME;
361362

362-
if (channelName != null) {
363-
return channelName;
364-
} else {
365-
return DEFAULT_CHANNEL_NAME;
363+
if (soundName != null) {
364+
channelName = soundName;
366365
}
366+
367+
return channelName;
367368
}
368369

369370
/**
@@ -434,4 +435,33 @@ boolean isEmptyBody(Bundle extras) {
434435
}
435436
}
436437

438+
@Nullable
439+
private static AudioAttributes getAudioAttributes() {
440+
AudioAttributes audioAttributes = null;
441+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
442+
audioAttributes = new AudioAttributes.Builder()
443+
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
444+
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
445+
.build();
446+
}
447+
return audioAttributes;
448+
}
449+
450+
private static Uri getSoundUri(Context context, String soundName, String soundUrl) {
451+
int soundId = 0;
452+
453+
if (soundUrl != null) {
454+
return Uri.parse(soundUrl);
455+
}
456+
457+
if (soundName != null) {
458+
soundId = context.getResources().getIdentifier(soundName, IterableConstants.SOUND_FOLDER_IDENTIFIER, context.getPackageName());
459+
}
460+
461+
if (soundId == 0) {
462+
return Settings.System.DEFAULT_NOTIFICATION_URI;
463+
}
464+
465+
return Uri.parse(IterableConstants.ANDROID_RESOURCE_PATH + context.getPackageName() + "/" + soundId);
466+
}
437467
}

0 commit comments

Comments
 (0)