diff --git a/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java
index ca8b1ecd5..6f50e8409 100644
--- a/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java
+++ b/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java
@@ -24,16 +24,19 @@ import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.List;
-public abstract class AbstractNotificationBuilder extends NotificationCompat.Builder {
+abstract class AbstractNotificationBuilder extends NotificationCompat.Builder {
@SuppressWarnings("unused")
private static final String TAG = AbstractNotificationBuilder.class.getSimpleName();
protected Context context;
protected NotificationPrivacyPreference privacy;
+ private int notificationId;
+ private Uri ringtone;
+ private boolean vibrate;
AbstractNotificationBuilder(Context context, NotificationPrivacyPreference privacy, boolean signal) {
- super(context, createMsgNotificationChannel(context, signal));
+ super(context, createMsgNotificationChannel(context));
this.context = context;
this.privacy = privacy;
@@ -50,18 +53,17 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
return builder;
}
+ // Alarms are not set in the notification or the notification channel but handled separately
+ // by the MessageNotifier. It allows us to dynamically turn on and off the sounds and as well as
+ // to change vibration and sounds during runtime
void setAlarms(@Nullable Uri ringtone, Prefs.VibrateState vibrate) {
Uri defaultRingtone = Prefs.getNotificationRingtone(context);
boolean defaultVibrate = Prefs.isNotificationVibrateEnabled(context);
+ if (ringtone == null && !TextUtils.isEmpty(defaultRingtone.toString())) this.ringtone = defaultRingtone;
+ else if (ringtone != null && !ringtone.toString().isEmpty()) this.ringtone = ringtone;
- if (ringtone == null && !TextUtils.isEmpty(defaultRingtone.toString())) setSound(defaultRingtone);
- else if (ringtone != null && !ringtone.toString().isEmpty()) setSound(ringtone);
-
- if (vibrate == Prefs.VibrateState.ENABLED ||
- (vibrate == Prefs.VibrateState.DEFAULT && defaultVibrate))
- {
- setDefaults(Notification.DEFAULT_VIBRATE);
- }
+ this.vibrate = (vibrate == Prefs.VibrateState.ENABLED ||
+ (vibrate == Prefs.VibrateState.DEFAULT && defaultVibrate));
}
private void setLed() {
@@ -79,8 +81,8 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
argb = Color.rgb(0xFF, 0xFF, 0xFF);
}
setLights(argb,
- Integer.parseInt(blinkPatternArray[0]),
- Integer.parseInt(blinkPatternArray[1]));
+ Integer.parseInt(blinkPatternArray[0]),
+ Integer.parseInt(blinkPatternArray[1]));
}
}
@@ -106,10 +108,9 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
// - NotificationChannels have default values that have a higher precedence as the Notification.Builder setting
// - once created, NotificationChannels cannot be modified programmatically
// - NotificationChannels can be deleted, however, on re-creation it becomes un-deleted with the old settings
- // - the idea is that sound, led, vibrate is edited by the user
- // via the ACTION_CHANNEL_NOTIFICATION_SETTINGS intent that takes the channelId
+ // - the idea is that sound and vibrate are handled outside of the scope of the notification channel
- private static String createMsgNotificationChannel(Context context, boolean signal) {
+ private static String createMsgNotificationChannel(Context context) {
String chBase = "ch_msg2_";
String chId = chBase + "unsupported";
@@ -119,16 +120,11 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
// get all values we'll use as settings for the NotificationChannel
String ledColor = Prefs.getNotificationLedColor(context);
- boolean defaultVibrate = Prefs.isNotificationVibrateEnabled(context);
- Uri ringtone = Prefs.getNotificationRingtone(context);
// compute hash from these settings
String hash = "";
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(ledColor.getBytes());
- md.update(defaultVibrate ? (byte) 1 : (byte) 0);
- md.update(ringtone.toString().getBytes());
- md.update(signal ? (byte) 1 : (byte) 0);
hash = String.format("%X", new BigInteger(1, md.digest())).substring(0, 16);
// get channel name
@@ -139,7 +135,7 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
notificationManager.deleteNotificationChannel(oldChId);
}
catch (Exception e) {
- ; // channel not created before
+ // channel not created before
}
Prefs.setStringPreference(context, "ch_curr_" + chBase, chId);
}
@@ -157,7 +153,7 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
// we cannot change the settings, however, this is handled by using different values for chId
if(!channelExists) {
NotificationChannel channel = new NotificationChannel(chId,
- "New messages", NotificationManager.IMPORTANCE_DEFAULT);
+ "New messages", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("Informs about new messages.");
if (!ledColor.equals("none")) {
@@ -174,19 +170,8 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
channel.enableLights(false);
}
- if (signal) {
- channel.enableVibration(defaultVibrate);
-
- if (!TextUtils.isEmpty(ringtone.toString())) {
- channel.setSound(ringtone,
- new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
- .build());
- }
- } else {
- channel.setSound(null, null);
- channel.enableVibration(false);
- }
+ channel.setSound(null, null);
+ channel.enableVibration(false);
notificationManager.createNotificationChannel(channel);
}
@@ -202,4 +187,21 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
private static boolean notificationChannelsSupported() {
return Build.VERSION.SDK_INT >= 26;
}
+
+
+ public void setNotificationId(int notificationId) {
+ this.notificationId = notificationId;
+ }
+
+ public int getNotificationId() {
+ return this.notificationId;
+ }
+
+ public Uri getRingtone() {
+ return this.ringtone;
+ }
+
+ public boolean getVibrate() {
+ return this.vibrate;
+ }
}
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
index 4065f2b95..2e8c03f2b 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
@@ -1,27 +1,12 @@
-/*
- * Copyright (C) 2011 Whisper Systems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
package org.thoughtcrime.securesms.notifications;
-import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.content.Context;
import android.media.AudioManager;
+import android.media.RingtoneManager;
import android.media.SoundPool;
-import android.service.notification.StatusBarNotification;
+import android.net.Uri;
+import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationManagerCompat;
import android.text.TextUtils;
@@ -46,334 +31,353 @@ import java.util.concurrent.TimeUnit;
import static org.thoughtcrime.securesms.notifications.MessageNotifierCompat.NO_VISIBLE_CHAT_ID;
import static org.thoughtcrime.securesms.notifications.MessageNotifierCompat.SUMMARY_NOTIFICATION_ID;
+abstract class MessageNotifier implements IMessageNotifier {
-/**
- * Handles posting system notifications for new messages.
- *
- *
- * @author Moxie Marlinspike
- */
+ static final String TAG = org.thoughtcrime.securesms.notifications.MessageNotifierApi23.class.getSimpleName();
-@TargetApi(23)
-class MessageNotifier implements IMessageNotifier {
+ private static final String NOTIFICATION_GROUP = "messages";
+ private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(20);
+ private static final long STARTUP_SILENCE_DELTA = TimeUnit.MINUTES.toMillis(1);
+ private static final long INITIAL_STARTUP = System.currentTimeMillis();
- private static final String TAG = MessageNotifier.class.getSimpleName();
+ static volatile int visibleChatId = NO_VISIBLE_CHAT_ID;
+ static volatile long lastAudibleNotification = -1;
+ final NotificationState notificationState;
+ final Context appContext;
+ private final SoundPool soundPool;
+ private final int soundIn;
+ private final int soundOut;
+ private boolean soundInLoaded;
+ private boolean soundOutLoaded;
- private static final String NOTIFICATION_GROUP = "messages";
- private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(20);
- private static final long STARTUP_SILENCE_DELTA = TimeUnit.MINUTES.toMillis(1);
- private static final long INITIAL_STARTUP = System.currentTimeMillis();
+ MessageNotifier(Context context) {
+ appContext = context.getApplicationContext();
+ soundPool = new SoundPool(3, AudioManager.STREAM_SYSTEM, 0);
+ soundIn = soundPool.load(context, R.raw.sound_in, 1);
+ soundOut = soundPool.load(context, R.raw.sound_out, 1);
+ notificationState = new NotificationState();
- static volatile int visibleChatId = NO_VISIBLE_CHAT_ID;
- static volatile long lastAudibleNotification = -1;
- final NotificationState notificationState;
- final Context appContext;
- private final SoundPool soundPool;
- private final int soundIn;
- private final int soundOut;
- private boolean soundInLoaded;
- private boolean soundOutLoaded;
+ soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> {
+ if (status == 0) {
+ if (sampleId == soundIn) {
+ soundInLoaded = true;
+ } else if (sampleId == soundOut) {
+ soundOutLoaded = true;
+ }
+ }
+ });
+ }
- MessageNotifier(Context context) {
- appContext = context.getApplicationContext();
- soundPool = new SoundPool(3, AudioManager.STREAM_SYSTEM, 0);
- soundIn = soundPool.load(context, R.raw.sound_in, 1);
- soundOut = soundPool.load(context, R.raw.sound_out, 1);
- notificationState = new NotificationState();
-
- soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> {
- if (status == 0) {
- if (sampleId == soundIn) {
- soundInLoaded = true;
- } else if (sampleId == soundOut) {
- soundOutLoaded = true;
+ @Override
+ public void playSendSound() {
+ if (Prefs.isInChatNotifications(appContext) && soundOutLoaded) {
+ soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f);
}
- }
- });
- }
-
- @Override
- public void playSendSound() {
- if (Prefs.isInChatNotifications(appContext) && soundOutLoaded) {
- soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f);
- }
- }
-
- @Override
- public void updateVisibleChat(int chatId) {
- visibleChatId = chatId;
- if (visibleChatId != NO_VISIBLE_CHAT_ID) {
- removeNotifications(visibleChatId);
- }
- }
-
- @Override
- public void updateNotification(int chatId, int messageId) {
- boolean isVisible = visibleChatId == chatId;
- ApplicationDcContext dcContext = DcHelper.getContext(appContext);
-
- if (isVisible) {
- dcContext.marknoticedChat(chatId);
}
- if (!Prefs.isNotificationsEnabled(appContext) ||
- Prefs.isChatMuted(appContext, chatId))
+ @Override
+ public void updateVisibleChat(int chatId) {
+ visibleChatId = chatId;
+ if (visibleChatId != NO_VISIBLE_CHAT_ID) {
+ removeNotifications(visibleChatId);
+ }
+ }
+
+ @Override
+ public void updateNotification(int chatId, int messageId) {
+ boolean isVisible = visibleChatId == chatId;
+ ApplicationDcContext dcContext = DcHelper.getContext(appContext);
+
+ if (isVisible) {
+ dcContext.marknoticedChat(chatId);
+ }
+
+ if (!Prefs.isNotificationsEnabled(appContext) ||
+ Prefs.isChatMuted(appContext, chatId))
+ {
+ return;
+ }
+
+ if (isVisible) {
+ sendInChatNotification(chatId);
+ } else if (visibleChatId != NO_VISIBLE_CHAT_ID) {
+ //different chat
+ sendNotifications(chatId, messageId, false);
+ } else {
+ //app is in background or different Activity is on top
+ sendNotifications(chatId, messageId, true);
+ }
+ }
+
+ /**
+ * On notification privacy preference changed,
+ * the notification state needs to be updated.
+ */
+ @Override
+ public void onNotificationPrivacyChanged() {
+ if (!Prefs.isNotificationsEnabled(appContext)) {
+ return;
+ }
+
+ clearNotifications();
+ ApplicationDcContext dcContext = DcHelper.getContext(appContext);
+ int[] freshMessages = dcContext.getFreshMsgs();
+ for (int message : freshMessages) {
+ DcMsg record = dcContext.getMsg(message);
+ updateNotification(record.getChatId(), record.getId());
+ }
+ }
+
+ @Override
+ public void removeNotifications(int[] chatIds) {
+ List removedItems = new LinkedList<>();
+ for (int id : chatIds) {
+ removedItems.addAll(notificationState.removeNotificationsForChat(id));
+ }
+ cancelNotifications(removedItems);
+ recreateSummaryNotification();
+ }
+
+ @Override
+ public void removeNotifications(int chatId) {
+ List removedItems = notificationState.removeNotificationsForChat(chatId);
+ cancelNotifications(removedItems);
+ recreateSummaryNotification();
+ }
+
+ void cancelNotifications(List removedItems) {
+ NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
+ for (NotificationItem item : removedItems) {
+ notifications.cancel(item.getId());
+ }
+ }
+
+ private void recreateSummaryNotification() {
+ NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
+ notifications.cancel(SUMMARY_NOTIFICATION_ID);
+
+ if (notificationState.hasMultipleChats()) {
+ for (Integer id : notificationState.getChats()) {
+ sendSingleChatNotification(appContext, new NotificationState(notificationState.getNotificationsForChat(id)), false, true);
+ }
+ sendMultipleChatNotification(appContext, notificationState, false);
+ } else {
+ sendSingleChatNotification(appContext, notificationState, false, false);
+ }
+ }
+
+ void cancelActiveNotifications() {
+ NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
+ notifications.cancel(SUMMARY_NOTIFICATION_ID);
+ }
+
+ void sendNotifications(int chatId, int messageId, boolean signal) {
+ ApplicationDcContext dcContext = DcHelper.getContext(appContext);
+ if (signal = isSignalAllowed(signal)) {
+ lastAudibleNotification = System.currentTimeMillis();
+ }
+
+ addMessageToNotificationState(dcContext, chatId, messageId);
+ if (notificationState.hasMultipleChats()) {
+ for (int id : notificationState.getChats()) {
+ sendSingleChatNotification(appContext, new NotificationState(notificationState.getNotificationsForChat(id)), false, true);
+ }
+ sendMultipleChatNotification(appContext, notificationState, signal);
+ } else {
+ sendSingleChatNotification(appContext, notificationState, signal, false);
+ }
+ }
+
+ boolean isSignalAllowed(boolean signalRequested) {
+ long now = System.currentTimeMillis();
+ return signalRequested && (
+ now - INITIAL_STARTUP) > STARTUP_SILENCE_DELTA &&
+ (now - lastAudibleNotification) > MIN_AUDIBLE_PERIOD_MILLIS;
+ }
+
+ private void clearNotifications() {
+ notificationState.reset();
+ cancelActiveNotifications();
+ }
+
+ void sendSingleChatNotification(@NonNull Context context,
+ @NonNull NotificationState notificationState,
+ boolean signal,
+ boolean bundled)
{
- return;
+ AbstractNotificationBuilder notificationBuilder = createSingleChatNotification(context, notificationState, signal, bundled);
+ if (notificationBuilder != null)
+ notify(context, notificationBuilder.getNotificationId(), notificationBuilder, signal);
}
- if (isVisible) {
- sendInChatNotification(chatId);
- } else if (visibleChatId != NO_VISIBLE_CHAT_ID) {
- //different chat
- sendNotifications(chatId, messageId, false);
- } else {
- //app is in background or different Activity is on top
- sendNotifications(chatId, messageId, true);
- }
- }
-
- /**
- * On notification privacy preference changed,
- * the notification state needs to be updated.
- */
- @Override
- public void onNotificationPrivacyChanged() {
- if (!Prefs.isNotificationsEnabled(appContext)) {
- return;
- }
-
- clearNotifications();
- ApplicationDcContext dcContext = DcHelper.getContext(appContext);
- int[] freshMessages = dcContext.getFreshMsgs();
- for (int message : freshMessages) {
- DcMsg record = dcContext.getMsg(message);
- updateNotification(record.getChatId(), record.getId());
- }
- }
-
- @Override
- public void removeNotifications(int[] chatIds) {
- List removedItems = new LinkedList<>();
- for (int id : chatIds) {
- removedItems.addAll(notificationState.removeNotificationsForChat(id));
- }
- cancelNotifications(removedItems);
- recreateSummaryNotification();
- }
-
- @Override
- public void removeNotifications(int chatId) {
- List removedItems = notificationState.removeNotificationsForChat(chatId);
- cancelNotifications(removedItems);
- recreateSummaryNotification();
- }
-
- void cancelNotifications(List removedItems) {
- NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
- for (NotificationItem item : removedItems) {
- notifications.cancel(item.getId());
- }
- }
-
- private void recreateSummaryNotification() {
- NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
- notifications.cancel(SUMMARY_NOTIFICATION_ID);
-
- if (notificationState.hasMultipleChats()) {
- for (Integer id : notificationState.getChats()) {
- sendSingleChatNotification(appContext, new NotificationState(notificationState.getNotificationsForChat(id)), false, true);
- }
- sendMultipleChatNotification(appContext, notificationState, false);
- } else {
- sendSingleChatNotification(appContext, notificationState, false, false);
- }
- }
-
- private void cancelActiveNotifications() {
- NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
- notifications.cancel(SUMMARY_NOTIFICATION_ID);
- try {
- StatusBarNotification[] activeNotifications = notifications.getActiveNotifications();
-
- for (StatusBarNotification activeNotification : activeNotifications) {
- notifications.cancel(activeNotification.getId());
- }
- } catch (Throwable e) {
- // XXX Appears to be a ROM bug, see #6043
- Log.w(TAG, e);
- notifications.cancelAll();
- }
- }
-
- void sendNotifications(int chatId, int messageId, boolean signal) {
- ApplicationDcContext dcContext = DcHelper.getContext(appContext);
- if (signal = isSignalAllowed(signal)) {
- lastAudibleNotification = System.currentTimeMillis();
- }
-
- addMessageToNotificationState(dcContext, chatId, messageId);
- if (notificationState.hasMultipleChats()) {
- for (int id : notificationState.getChats()) {
- sendSingleChatNotification(appContext, new NotificationState(notificationState.getNotificationsForChat(id)), false, true);
- }
- sendMultipleChatNotification(appContext, notificationState, signal);
- } else {
- sendSingleChatNotification(appContext, notificationState, signal, false);
- }
- }
-
- boolean isSignalAllowed(boolean signalRequested) {
- long now = System.currentTimeMillis();
- return signalRequested && (
- now - INITIAL_STARTUP) > STARTUP_SILENCE_DELTA &&
- (now - lastAudibleNotification) > MIN_AUDIBLE_PERIOD_MILLIS;
- }
-
- private void clearNotifications() {
- notificationState.reset();
- cancelActiveNotifications();
- }
-
- void sendSingleChatNotification(@NonNull Context context,
- @NonNull NotificationState notificationState,
- boolean signal,
- boolean bundled)
- {
- if (notificationState.getNotifications().isEmpty()) {
- if (!bundled) cancelActiveNotifications();
- return;
- }
-
- SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, Prefs.getNotificationPrivacy(context), signal);
- List notifications = notificationState.getNotifications();
- NotificationItem firstItem = notifications.get(0);
- Recipient recipient = firstItem.getRecipient();
- int chatId = firstItem.getChatId();
- int notificationId = (SUMMARY_NOTIFICATION_ID + (bundled ? chatId : 0));
-
- builder.setChat(firstItem.getRecipient());
- builder.setMessageCount(notificationState.getMessageCount());
- builder.setPrimaryMessageBody(recipient, firstItem.getIndividualRecipient(),
- firstItem.getText(""), firstItem.getSlideDeck());
- builder.setContentIntent(firstItem.getPendingIntent(context));
- builder.setGroup(NOTIFICATION_GROUP);
- builder.setDeleteIntent(notificationState.getMarkAsReadIntent(context, chatId, notificationId));
-
- long timestamp = firstItem.getTimestamp();
- if (timestamp != 0) builder.setWhen(timestamp);
-
- builder.addActions(notificationState.getMarkAsReadIntent(context, chatId, notificationId),
- notificationState.getQuickReplyIntent(context, recipient),
- notificationState.getRemoteReplyIntent(context, recipient));
-
- ListIterator iterator = notifications.listIterator(notifications.size());
-
- while(iterator.hasPrevious()) {
- NotificationItem item = iterator.previous();
- builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
- }
-
- if (signal) {
- builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate(context));
- builder.setTicker(firstItem.getIndividualRecipient(),
- firstItem.getText());
- }
-
- if (!bundled) {
- builder.setGroupSummary(true);
- }
-
- NotificationManagerCompat.from(context).notify(notificationId, builder.build());
- }
-
- void sendMultipleChatNotification(@NonNull Context context,
- @NonNull NotificationState notificationState,
- boolean signal)
- {
- MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, Prefs.getNotificationPrivacy(context), signal);
- List notifications = notificationState.getNotifications();
- NotificationItem firstItem = notifications.get(0);
-
- builder.setMessageCount(notificationState.getMessageCount(), notificationState.getChatCount());
- builder.setMostRecentSender(firstItem.getIndividualRecipient());
- builder.setGroup(NOTIFICATION_GROUP);
- builder.setDeleteIntent(notificationState.getMarkAsReadIntent(context, 0, SUMMARY_NOTIFICATION_ID));
-
- long timestamp = firstItem.getTimestamp();
- if (timestamp != 0) builder.setWhen(timestamp);
-
- builder.addActions(notificationState.getMarkAsReadIntent(context, 0, SUMMARY_NOTIFICATION_ID));
-
- ListIterator iterator = notifications.listIterator(notifications.size());
-
- while(iterator.hasPrevious()) {
- NotificationItem item = iterator.previous();
- builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
- }
-
- if (signal) {
- builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate(context));
- builder.setTicker(firstItem.getIndividualRecipient(),
- firstItem.getText());
- }
-
- NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build());
- }
-
- private void sendInChatNotification(int chatId) {
- if (!Prefs.isInChatNotifications(appContext) ||
- ServiceUtil.getAudioManager(appContext).getRingerMode() != AudioManager.RINGER_MODE_NORMAL)
+ void sendMultipleChatNotification(@NonNull Context context,
+ @NonNull NotificationState notificationState,
+ boolean signal)
{
- return;
+ AbstractNotificationBuilder notificationBuilder = createMultipleChatNotification(context, notificationState, signal);
+ if (notificationBuilder != null)
+ notify(context, SUMMARY_NOTIFICATION_ID, notificationBuilder, signal);
}
- if(Prefs.isChatMuted(appContext, chatId)) {
- Log.d(TAG, "chat muted");
- return;
+ protected AbstractNotificationBuilder createSingleChatNotification(@NonNull Context context,
+ @NonNull NotificationState notificationState,
+ boolean signal,
+ boolean bundled) {
+ if (notificationState.getNotifications().isEmpty()) {
+ if (!bundled) cancelActiveNotifications();
+ return null;
+ }
+
+ SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, Prefs.getNotificationPrivacy(context), signal);
+ List notifications = notificationState.getNotifications();
+ NotificationItem firstItem = notifications.get(0);
+ Recipient recipient = firstItem.getRecipient();
+ int chatId = firstItem.getChatId();
+ int notificationId = (SUMMARY_NOTIFICATION_ID + (bundled ? chatId : 0));
+
+ builder.setNotificationId(notificationId);
+ builder.setChat(firstItem.getRecipient());
+ builder.setMessageCount(notificationState.getMessageCount());
+ builder.setPrimaryMessageBody(recipient, firstItem.getIndividualRecipient(),
+ firstItem.getText(""), firstItem.getSlideDeck());
+ builder.setContentIntent(firstItem.getPendingIntent(context));
+ builder.setGroup(NOTIFICATION_GROUP);
+ builder.setDeleteIntent(notificationState.getMarkAsReadIntent(context, chatId, notificationId));
+
+ long timestamp = firstItem.getTimestamp();
+ if (timestamp != 0) builder.setWhen(timestamp);
+
+ builder.addActions(notificationState.getMarkAsReadIntent(context, chatId, notificationId),
+ notificationState.getQuickReplyIntent(context, recipient),
+ notificationState.getRemoteReplyIntent(context, recipient));
+
+ ListIterator iterator = notifications.listIterator(notifications.size());
+
+ while(iterator.hasPrevious()) {
+ NotificationItem item = iterator.previous();
+ builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
+ }
+
+ if (signal) {
+ builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate(context));
+ builder.setTicker(firstItem.getIndividualRecipient(),
+ firstItem.getText());
+ }
+
+ if (!bundled) {
+ builder.setGroupSummary(true);
+ }
+
+ return builder;
+
+
}
- if (soundInLoaded) {
- soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f);
- }
- }
+ void playNotificationSound(Uri ringtone, boolean vibrate) {
+ RingtoneManager.getRingtone(appContext, ringtone).play();
+ if (vibrate) {
- void addMessageToNotificationState(ApplicationDcContext dcContext, int chatId, int msgId) {
- if (Prefs.isChatMuted(appContext, chatId)) {
- return;
- }
-
- DcMsg record = dcContext.getMsg(msgId);
- if (record.isInfo()) {
- return;
- }
-
- int id = record.getId();
- CharSequence body = record.getDisplayBody();
- DcMsg dcMsg = dcContext.getMsg(msgId);
- Recipient chatRecipient = new Recipient(appContext, dcContext.getChat(dcMsg.getChatId()), null);
- Recipient individualRecipient = new Recipient(appContext, null, dcContext.getContact(dcMsg.getFromId()));
- SlideDeck slideDeck = new SlideDeck(dcContext.context, record);
- long timestamp = record.getTimestamp();
-
-
- if(slideDeck.getSlides().isEmpty())
- slideDeck = null;
-
- // TODO: if message content should be hidden on screen lock, do it here.
- if (record.hasFile() && TextUtils.isEmpty(body)) {
- String summaryText = record.getSummarytext(100);
- if (summaryText.isEmpty()) {
- body = SpanUtil.italic(appContext.getString(R.string.notify_media_message));
- } else {
- body = SpanUtil.italic(summaryText);
- }
- } else if (record.hasFile() && !record.isMediaPending()) {
- String message = appContext.getString(R.string.notify_media_message_with_text, body);
- int italicLength = message.length() - body.length();
- body = SpanUtil.italic(message, italicLength);
+ Vibrator v = (Vibrator) appContext.getSystemService(Context.VIBRATOR_SERVICE);
+ v.vibrate(100);
+ v.vibrate(200);
+ }
}
- notificationState.addNotification(new NotificationItem(id, chatRecipient, individualRecipient, chatId, body, timestamp, slideDeck));
- }
+
+ protected AbstractNotificationBuilder createMultipleChatNotification(@NonNull Context context,
+ @NonNull NotificationState notificationState,
+ boolean signal) {
+ MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, Prefs.getNotificationPrivacy(context), signal);
+ List notifications = notificationState.getNotifications();
+ NotificationItem firstItem = notifications.get(0);
+
+ builder.setMessageCount(notificationState.getMessageCount(), notificationState.getChatCount());
+ builder.setMostRecentSender(firstItem.getIndividualRecipient());
+ builder.setGroup(NOTIFICATION_GROUP);
+ builder.setDeleteIntent(notificationState.getMarkAsReadIntent(context, 0, SUMMARY_NOTIFICATION_ID));
+
+ long timestamp = firstItem.getTimestamp();
+ if (timestamp != 0) builder.setWhen(timestamp);
+
+ builder.addActions(notificationState.getMarkAsReadIntent(context, 0, SUMMARY_NOTIFICATION_ID));
+
+ ListIterator iterator = notifications.listIterator(notifications.size());
+
+ while(iterator.hasPrevious()) {
+ NotificationItem item = iterator.previous();
+ builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
+ }
+
+ if (signal) {
+ builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate(context));
+ builder.setTicker(firstItem.getIndividualRecipient(),
+ firstItem.getText());
+ }
+
+ return builder;
+ }
+
+ private void notify(Context context, int notificationId, AbstractNotificationBuilder notificationBuilder, boolean signal) {
+ if (signal) {
+ playNotificationSound(notificationBuilder.getRingtone(), notificationBuilder.getVibrate());
+ }
+ NotificationManagerCompat.from(context).notify(notificationId, notificationBuilder.build());
+ }
+
+ private void sendInChatNotification(int chatId) {
+ if (!Prefs.isInChatNotifications(appContext) ||
+ ServiceUtil.getAudioManager(appContext).getRingerMode() != AudioManager.RINGER_MODE_NORMAL)
+ {
+ return;
+ }
+
+ if(Prefs.isChatMuted(appContext, chatId)) {
+ Log.d(TAG, "chat muted");
+ return;
+ }
+
+ if (soundInLoaded) {
+ soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f);
+ }
+ }
+
+ void addMessageToNotificationState(ApplicationDcContext dcContext, int chatId, int msgId) {
+ if (Prefs.isChatMuted(appContext, chatId)) {
+ return;
+ }
+
+ DcMsg record = dcContext.getMsg(msgId);
+ if (record.isInfo()) {
+ return;
+ }
+
+ int id = record.getId();
+ CharSequence body = record.getDisplayBody();
+ DcMsg dcMsg = dcContext.getMsg(msgId);
+ Recipient chatRecipient = new Recipient(appContext, dcContext.getChat(dcMsg.getChatId()), null);
+ Recipient individualRecipient = new Recipient(appContext, null, dcContext.getContact(dcMsg.getFromId()));
+ SlideDeck slideDeck = new SlideDeck(dcContext.context, record);
+ long timestamp = record.getTimestamp();
+
+
+ if(slideDeck.getSlides().isEmpty())
+ slideDeck = null;
+
+ // TODO: if message content should be hidden on screen lock, do it here.
+ if (record.hasFile() && TextUtils.isEmpty(body)) {
+ String summaryText = record.getSummarytext(100);
+ if (summaryText.isEmpty()) {
+ body = SpanUtil.italic(appContext.getString(R.string.notify_media_message));
+ } else {
+ body = SpanUtil.italic(summaryText);
+ }
+ } else if (record.hasFile() && !record.isMediaPending()) {
+ String message = appContext.getString(R.string.notify_media_message_with_text, body);
+ int italicLength = message.length() - body.length();
+ body = SpanUtil.italic(message, italicLength);
+ }
+
+ notificationState.addNotification(new NotificationItem(id, chatRecipient, individualRecipient, chatId, body, timestamp, slideDeck));
+ }
}
+
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifierApi23.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifierApi23.java
new file mode 100644
index 000000000..2461392f9
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifierApi23.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.notifications;
+
+import android.annotation.TargetApi;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import org.thoughtcrime.securesms.util.ServiceUtil;
+
+
+/**
+ * Handles posting system notifications for new messages.
+ *
+ */
+
+@TargetApi(23)
+class MessageNotifierApi23 extends MessageNotifier {
+
+ MessageNotifierApi23(Context context) {
+ super(context);
+ }
+
+ void cancelActiveNotifications() {
+ super.cancelActiveNotifications();
+ NotificationManager notifications = ServiceUtil.getNotificationManager(appContext);
+ try {
+
+ StatusBarNotification[] activeNotifications = notifications.getActiveNotifications();
+ for (StatusBarNotification activeNotification : activeNotifications) {
+ notifications.cancel(activeNotification.getId());
+ }
+ } catch (Throwable e) {
+ // XXX Appears to be a ROM bug, see #6043
+ Log.w(TAG, e);
+ notifications.cancelAll();
+ }
+ }
+
+ }
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifierCompat.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifierCompat.java
index f2dd4abc9..610319668 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifierCompat.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifierCompat.java
@@ -20,7 +20,7 @@ public class MessageNotifierCompat {
if (Build.VERSION.SDK_INT < 23) {
instance = new MessageNotifierPreApi23(context);
} else {
- instance = new MessageNotifier(context);
+ instance = new MessageNotifierApi23(context);
}
}
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifierPreApi23.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifierPreApi23.java
index 8e6b37760..eb63ce5a9 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifierPreApi23.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifierPreApi23.java
@@ -10,7 +10,7 @@ import java.util.List;
import static org.thoughtcrime.securesms.notifications.MessageNotifierCompat.SUMMARY_NOTIFICATION_ID;
-public class MessageNotifierPreApi23 extends MessageNotifier {
+class MessageNotifierPreApi23 extends MessageNotifier {
MessageNotifierPreApi23(Context context) {
super(context);
@@ -39,7 +39,7 @@ public class MessageNotifierPreApi23 extends MessageNotifier {
void sendNotifications(int chatId, int messageId, boolean signal) {
ApplicationDcContext dcContext = DcHelper.getContext(appContext);
if (signal = isSignalAllowed(signal)) {
- lastAudibleNotification = System.currentTimeMillis();;
+ lastAudibleNotification = System.currentTimeMillis();
}
addMessageToNotificationState(dcContext, chatId, messageId);