I am having some troubles updating notification sound for a channel into Android Oreo. I know that the sound can be set by the user manually by opening App notifications screen, but I want to do this programmatically by using RingtonePreference into a default Settings activity (user to be able to pick up notification sound from an activity inside my app).
Problem is that the first notification fired into application picks up the default sound value from the PreferenceManager.getDefaultSharedPreferences() and after manually changing it to other media (using RingtonePreference screen) it will still play the sound which was created initially on that channel and not the new one selected by the user.
I don't understand why the NotificationChannel sound is not updated according with the new sound value as I am doing something like this
NotificationChannel mChannel = new NotificationChannel(id, title, importance);
mChannel.setSound(ringtoneUri, audioAttributes);
Below is the full code:
public static void sendNotification(Context context, NotificationType notificationType) {
String id = "channel_1"; // default_channel_id
String title = "Doppler Channel"; // Default Channel
Intent intent;
PendingIntent pendingIntent;
NotificationCompat.Builder builder;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Uri ringtoneUri = Uri.parse(preferences.getString("notifications_new_message_ringtone", "DEFAULT_RINGTONE_URI"));
boolean isNotificationSticky = !Boolean.parseBoolean(preferences.getAll().get("stickyNotification").toString());
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build();
NotificationManager notifManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel mChannel = notifManager.getNotificationChannel(id);
mChannel = new NotificationChannel(id, title, importance);
mChannel.setSound(ringtoneUri, audioAttributes);
notifManager.createNotificationChannel(mChannel);
builder = new NotificationCompat.Builder(context, id);
intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
builder .setSmallIcon(notificationType.getNotificationIcon())
.setContentTitle(notificationType.getNotificationContent())
.setSubText(notificationType.getNotificationTitle())
.setOnlyAlertOnce(true)
.setOngoing(isNotificationSticky)
.setAutoCancel(true)
.setSound(ringtoneUri)
.setColor(notificationType.getNotificationColor())
.setContentIntent(pendingIntent);
}
Notification notification = builder.build();
notifManager.notify(1, notification);
}
The only way I was able to update the sound was to give the channel id a random value UUID.randomUUID().toString()
each time a notification is fired but this is causing a lot of garbage when user manually checks the App notification screen.
A hint on this would be much appreciated.
Thanks a lot!
答案 0 :(得分:1)
I don't understand why the NotificationChannel sound is not updated according with the new sound value
For the most part, NotificationChannel
is a write-once API. You cannot modify most of its characteristics after you create it.
but I want to do this programmatically by using RingtonePreference into a default Settings activity
I recommend removing this feature from your app, or only offering it on Android 7.1 and older devices.
答案 1 :(得分:0)
有一种方法可以使您“欺骗” Android。如果您使用WhatsApp应用程序,则会看到它们允许您从应用程序内部更改消息通知的声音。而且,如果您进行一些调查,您会发现它们实际上是在删除并创建频道。
但是,如果您删除并重新创建相同频道(换句话说,“新”频道与“旧”频道具有相同的频道ID),那么您所做的新更改将不适用。 Android显然会跟踪所有已删除的文件,并在重新创建时阻止对其进行更新。因此,假设您拥有自己的频道(并可以在您的应用中访问其字段)。例如,如果要用户更新频道声音,您需要做的是:
因此,如果我们假设您有两个已定义“ Base ID”:s的频道,那么我们将基于它们的所有频道ID。然后,每当您的应用启动时,您都可以检查频道是否已经存在,如果存在,请检索它们,否则创建它们。当您添加更多渠道时,这需要一些体力劳动,也许有更好的方法可以做到这一点。但是,如果执行此操作,则可以为每次更改创建一个新通道,并为其分配新的递增ID。下面是解决所有这些问题的类的完整工作示例。在这个示例中,我们有两个带有baseId的通道,并且分别用声音#1和声音#2进行了初始化,但是每次实例化该类时,我们都会更新第二个通道并将其赋予声音#3。
public class TestNotificationHandler {
private static TestNotificationHandler instance;
private NotificationManager mNotificationManager;
private Context mContext;
private Uri NOTIFICATION_SOUND_1, NOTIFICATION_SOUND_2, NOTIFICATION_SOUND_3, NOTIFICATION_SOUND_4;
private static final String TAG = TestNotificationHandler.class.getSimpleName();
public static TestNotificationHandler getInstance(Context context) {
if (instance == null) {
instance = new TestNotificationHandler(context);
}
return instance;
}
private TestNotificationHandler(Context context) {
mContext = context;
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NOTIFICATION_SOUND_1 = Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.sound_1);
NOTIFICATION_SOUND_2 = Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.sound_2);
NOTIFICATION_SOUND_3 = Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.sound_3);
NOTIFICATION_SOUND_4 = Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.sound_4);
getOrCreateChannels();
// Only added here for testing, this should of course be done when user actually performs a change from within the app
setNewChannelSound(testChannel2, NOTIFICATION_SOUND_3);
// Remember that we now effectively have deleted testChannel2, so running this will not work, b/c no channel with the id that the testChannel2 object has currently exists so we cannot delete it
setNewChannelSound(testChannel2, NOTIFICATION_SOUND_4);
// The easy and ugly way would be to simply do this which will update our objects
getOrCreateChannels();
// And this will now work
setNewChannelSound(testChannel2, NOTIFICATION_SOUND_4);
// If we changed so that setNewChannelSound and updateChannel actually returned the updated
// channel (or the already existing one if could not update), then we could do something like:
// testChannel2 = setNewChannelSound(testChannel2, NOTIFICATION_SOUND_3);
}
AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build();
private NotificationChannel testChannel;
private String baseTestChannelId = "TEST_CHANNEL_ID-";
private NotificationChannel testChannel2;
private String baseTest2ChannelId = "TEST_CHANNEL_2_ID-";
private void getOrCreateChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
for (NotificationChannel channel : mNotificationManager.getNotificationChannels()) {
// Since we'll incrementally update the ID we'll look for the correct channel with startsWith
if (channel.getId().startsWith(baseTestChannelId)) {
testChannel = channel;
} else if (channel.getId().startsWith(baseTest2ChannelId)) {
testChannel2 = channel;
}
}
if (testChannel == null) {
// This should only happen the first time the app is launched or if you just added this channel to your app
// We'll update the ID incrementally so if it doesn't exist will simply create it as number 0
testChannel = new NotificationChannel(baseTestChannelId + "0", "TEST CHANNEL", NotificationManager.IMPORTANCE_HIGH);
testChannel.setDescription("First test channel");
testChannel.setSound(NOTIFICATION_SOUND_1, mAudioAttributes);
mNotificationManager.createNotificationChannel(testChannel);
}
if (testChannel2 == null) {
// This should only happen the first time the app is launched or if you just added this channel to your app
// We'll update the ID incrementally so if it doesn't exist will simply create it as number 0
testChannel2 = new NotificationChannel(baseTest2ChannelId + "0", "TEST CHANNEL 2", NotificationManager.IMPORTANCE_HIGH);
testChannel2.setDescription("Second test channel");
testChannel2.setSound(NOTIFICATION_SOUND_2, mAudioAttributes);
mNotificationManager.createNotificationChannel(testChannel2);
}
}
}
private boolean setNewChannelSound(NotificationChannel notificationChannel, Uri newSound) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel.setSound(newSound, mAudioAttributes);
return updateChannel(notificationChannel);
}
return false;
}
private boolean updateChannel(NotificationChannel notificationChannel) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String baseChannelId = getBaseChannelId(notificationChannel);
if (baseChannelId == null) {
Log.e(TAG, "Could not find baseChannelId from id: " + notificationChannel.getId());
return false;
}
int oldIndex = getChannelIncrementedIndex(notificationChannel, baseChannelId);
if (oldIndex == -1) {
Log.e(TAG, String.format("Could not get old channel index from id: %s and baseChannelId: %d", notificationChannel.getId(), baseChannelId));
return false;
}
NotificationChannel updatedChannel = new NotificationChannel(baseChannelId+(oldIndex+1), notificationChannel.getName(), NotificationManager.IMPORTANCE_HIGH);
updatedChannel.setDescription(notificationChannel.getDescription());
updatedChannel.setVibrationPattern(notificationChannel.getVibrationPattern());
updatedChannel.setSound(notificationChannel.getSound(), mAudioAttributes);
mNotificationManager.deleteNotificationChannel(notificationChannel.getId());
mNotificationManager.createNotificationChannel(updatedChannel);
return true;
}
return false;
}
/**********************************************************
Some helper methods
**********************************************************/
private String getBaseChannelId(NotificationChannel notificationChannel) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (notificationChannel.getId().startsWith(baseTestChannelId)) {
return baseTestChannelId;
} else if (notificationChannel.getId().startsWith(baseTest2ChannelId)) {
return baseTest2ChannelId;
}
}
return null;
}
private int getChannelIncrementedIndex(NotificationChannel channel, String baseChannelId) {
int index = -1;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelIncIdx = channel.getId().substring(baseChannelId.length(), channel.getId().length());
try {
index = Integer.parseInt(channelIncIdx);
} catch (Exception e) {
Log.e(TAG, String.format("Could not parse channel index %s", channelIncIdx));
}
}
return index;
}
}
我要说的唯一缺点是: