remove dead code

This commit is contained in:
B. Petersen
2020-05-25 14:30:35 +02:00
parent e8a3bae004
commit fe0b1cecd2
10 changed files with 0 additions and 1484 deletions
@@ -1,214 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.List;
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) {
super(context, createMsgNotificationChannel(context));
this.context = context;
this.privacy = privacy;
setLed();
}
CharSequence getStyledMessage(@NonNull Recipient recipient, @Nullable CharSequence message) {
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(Util.getBoldedString(recipient.toShortString()));
builder.append(": ");
builder.append(message == null ? "" : message);
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(int systemRingerMode, @Nullable Uri ringtone, Prefs.VibrateState vibrate) {
Uri appDefaultRingtone = Prefs.getNotificationRingtone(context);
boolean appDefaultVibrate = Prefs.isNotificationVibrateEnabled(context);
if (systemRingerMode == AudioManager.RINGER_MODE_NORMAL) {
if (ringtone == null && !TextUtils.isEmpty(appDefaultRingtone.toString())) {
this.ringtone = appDefaultRingtone;
} else if (ringtone != null && !ringtone.toString().isEmpty()) {
this.ringtone = ringtone;
}
}
this.vibrate = (systemRingerMode != AudioManager.RINGER_MODE_SILENT) &&
(vibrate == Prefs.VibrateState.ENABLED ||
(vibrate == Prefs.VibrateState.DEFAULT && appDefaultVibrate));
}
private void setLed() {
/*
String ledColor = Prefs.getNotificationLedColor(context);
String ledBlinkPattern = Prefs.getNotificationLedPattern(context);
String ledBlinkPatternCustom = Prefs.getNotificationLedPatternCustom(context);
if (!ledColor.equals("none")) {
String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom);
int argb;
try {
argb = Color.parseColor(ledColor);
}
catch (Exception e) {
argb = Color.rgb(0xFF, 0xFF, 0xFF);
}
setLights(argb,
Integer.parseInt(blinkPatternArray[0]),
Integer.parseInt(blinkPatternArray[1]));
}
*/
}
void setTicker(@NonNull Recipient recipient, @Nullable CharSequence message) {
if (privacy.isDisplayMessage()) {
setTicker(getStyledMessage(recipient, message));
} else if (privacy.isDisplayContact()) {
setTicker(getStyledMessage(recipient, context.getString(R.string.notify_new_message)));
} else {
setTicker(context.getString(R.string.notify_new_message));
}
}
private String[] parseBlinkPattern(String blinkPattern, String blinkPatternCustom) {
if (blinkPattern.equals("custom"))
blinkPattern = blinkPatternCustom;
return blinkPattern.split(",");
}
// handle NotificationChannels:
// - since oreo, a NotificationChannel is a MUST
// - 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 and vibrate are handled outside of the scope of the notification channel
private static String createMsgNotificationChannel(Context context) {
String chBase = "ch_msg3_";
String chId = chBase + "unsupported";
if(notificationChannelsSupported()) {
try {
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
// get all values we'll use as settings for the NotificationChannel
String ledColor = Prefs.getNotificationLedColor(context);
// compute hash from these settings
String hash = "";
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(ledColor.getBytes());
hash = String.format("%X", new BigInteger(1, md.digest())).substring(0, 16);
// get channel name
chId = chBase + hash;
String oldChId = Prefs.getStringPreference(context, "ch_curr_" + chBase, "");
if (!oldChId.equals(chId)) {
try {
notificationManager.deleteNotificationChannel(oldChId);
}
catch (Exception e) {
// channel not created before
}
Prefs.setStringPreference(context, "ch_curr_" + chBase, chId);
}
// check if there is already a channel with the given name
List<NotificationChannel> channels = notificationManager.getNotificationChannels();
boolean channelExists = false;
for (int i = 0; i < channels.size(); i++) {
if (chId.equals(channels.get(i).getId())) {
channelExists = true;
}
}
// create a channel with the given settings;
// 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_HIGH);
channel.setDescription("Informs about new messages.");
if (!ledColor.equals("none")) {
channel.enableLights(true);
int argb;
try {
argb = Color.parseColor(ledColor);
}
catch (Exception e) {
argb = Color.rgb(0xFF, 0xFF, 0xFF);
}
channel.setLightColor(argb);
} else {
channel.enableLights(false);
}
channel.setSound(null, null);
channel.enableVibration(false);
notificationManager.createNotificationChannel(channel);
}
}
catch(Exception e) {
e.printStackTrace();
}
}
return chId;
}
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;
}
}
@@ -1,40 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.os.AsyncTask;
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
/**
* This marks the messages the user has acknowledged as noticed.
* @author Angelo Fuchs
*/
public class MarkAsNoticedAsyncTask extends AsyncTask<Void, Void, Void> {
private final int[] ids;
private final ApplicationDcContext dcContext;
private final boolean isChat;
/**
* goes through the given messages or chats and marks them as notified.
* @param ids chat or messages ids to be marked.
* @param dcContext the applications context
* @param isChat true if the ids are chat ids, false to signify message ids.
*/
MarkAsNoticedAsyncTask(int[] ids, ApplicationDcContext dcContext, boolean isChat) {
this.ids = ids;
this.dcContext = dcContext;
this.isChat = isChat;
}
@Override
protected Void doInBackground(Void... params) {
for (int id : ids) {
if (isChat)
dcContext.marknoticedChat(id);
else
dcContext.marknoticedChat(dcContext.getMsg(id).getChatId());
}
return null;
}
}
@@ -1,412 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.app.NotificationManager;
import android.content.Context;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Vibrator;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationManagerCompat;
import android.text.TextUtils;
import android.util.Log;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
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 {
static final String TAG = org.thoughtcrime.securesms.notifications.MessageNotifierApi23.class.getSimpleName();
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();
static volatile int visibleChatId = NO_VISIBLE_CHAT_ID;
static volatile long lastAudibleNotification = -1;
final NotificationState notificationState;
final Context appContext;
final Object lock;
private final SoundPool soundPool;
private final AudioManager audioManager;
private final int soundIn;
private final int soundOut;
private boolean soundInLoaded;
private boolean soundOutLoaded;
MessageNotifier(Context context) {
appContext = context.getApplicationContext();
soundPool = new SoundPool(3, AudioManager.STREAM_SYSTEM, 0);
audioManager = ServiceUtil.getAudioManager(appContext);
soundIn = soundPool.load(context, R.raw.sound_in, 1);
soundOut = soundPool.load(context, R.raw.sound_out, 1);
notificationState = new NotificationState();
lock = new Object();
soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> {
if (status == 0) {
if (sampleId == soundIn) {
soundInLoaded = true;
} else if (sampleId == soundOut) {
soundOutLoaded = true;
}
}
});
}
public void playSendSound() {
if (Prefs.isInChatNotifications(appContext) && soundOutLoaded) {
soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f);
}
}
public void updateVisibleChat(int chatId) {
visibleChatId = chatId;
if (visibleChatId != NO_VISIBLE_CHAT_ID) {
removeNotifications(visibleChatId);
}
}
void updateNotification(int chatId, int messageId) {
updateNotification(DcHelper.getContext(appContext).getChat(chatId), messageId);
}
private void updateNotification(DcChat chat, int messageId) {
boolean isVisible = visibleChatId == chat.getId();
if (!Prefs.isNotificationsEnabled(appContext) ||
Prefs.isChatMuted(chat))
{
return;
}
if (isVisible) {
sendInChatNotification(chat);
} else if (visibleChatId != NO_VISIBLE_CHAT_ID) {
//different chat is on top
sendNotifications(chat, messageId, false);
} else {
//app is in background or different Activity is on top
sendNotifications(chat, messageId, true);
}
}
/**
* On notification privacy preference changed,
* the notification state needs to be updated.
*/
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(dcContext.getChat(record.getChatId()), record.getId());
}
}
public void removeNotifications(int[] chatIds) {
List<NotificationItem> removedItems = new LinkedList<>();
synchronized (lock) {
for (int id : chatIds) {
removedItems.addAll(notificationState.removeNotificationsForChat(id));
}
}
cancelNotifications(removedItems);
recreateSummaryNotification();
}
public void removeNotifications(int chatId) {
List<NotificationItem> removedItems;
synchronized (lock) {
removedItems = notificationState.removeNotificationsForChat(chatId);
}
cancelNotifications(removedItems);
recreateSummaryNotification();
}
void cancelNotifications(List<NotificationItem> 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);
synchronized (lock) {
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(DcChat chat, int messageId, boolean signal) {
ApplicationDcContext dcContext = DcHelper.getContext(appContext);
if (signal = isSignalAllowed(signal)) {
lastAudibleNotification = System.currentTimeMillis();
}
if (chat.isDeviceTalk()) {
// currently, we just never notify on device chat.
// esp. on first start, this is annoying.
return;
}
synchronized (lock) {
addMessageToNotificationState(dcContext, chat, 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() {
synchronized (lock) {
notificationState.reset();
}
cancelActiveNotifications();
}
void sendSingleChatNotification(@NonNull Context context,
@NonNull NotificationState notificationState,
boolean signal,
boolean bundled)
{
AbstractNotificationBuilder notificationBuilder = createSingleChatNotification(context, notificationState, signal, bundled);
if (notificationBuilder != null)
notify(context, notificationBuilder.getNotificationId(), notificationBuilder, signal);
}
void sendMultipleChatNotification(@NonNull Context context,
@NonNull NotificationState notificationState,
boolean signal)
{
AbstractNotificationBuilder notificationBuilder = createMultipleChatNotification(context, notificationState, signal);
if (notificationBuilder != null)
notify(context, SUMMARY_NOTIFICATION_ID, notificationBuilder, signal);
}
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));
List<NotificationItem> 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.getRemoteReplyIntent(context, recipient));
ListIterator<NotificationItem> iterator = notifications.listIterator(notifications.size());
while(iterator.hasPrevious()) {
NotificationItem item = iterator.previous();
builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
}
if (signal) {
builder.setAlarms(audioManager.getRingerMode(),
notificationState.getRingtone(context),
notificationState.getVibrate(context));
builder.setTicker(firstItem.getIndividualRecipient(),
firstItem.getText());
}
if (!bundled) {
builder.setGroupSummary(true);
}
return builder;
}
private void playNotificationSound(Uri uri, boolean vibrate) {
if(uri != null) {
Ringtone ringtone = RingtoneManager.getRingtone(appContext, uri);
if (ringtone != null) {
ringtone.play();
}
} // else we selected "no sound"
if (vibrate) {
Vibrator v = (Vibrator) appContext.getSystemService(Context.VIBRATOR_SERVICE);
if (v!=null) {
v.vibrate(100);
v.vibrate(200);
}
}
}
protected AbstractNotificationBuilder createMultipleChatNotification(@NonNull Context context,
@NonNull NotificationState notificationState,
boolean signal) {
MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, Prefs.getNotificationPrivacy(context));
List<NotificationItem> 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<NotificationItem> iterator = notifications.listIterator(notifications.size());
while(iterator.hasPrevious()) {
NotificationItem item = iterator.previous();
builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
}
if (signal) {
builder.setAlarms(audioManager.getRingerMode(),
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(DcChat chat) {
if (!Prefs.isInChatNotifications(appContext) ||
audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL)
{
return;
}
if(Prefs.isChatMuted(chat)) {
Log.d(TAG, "chat muted");
return;
}
if (soundInLoaded) {
soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f);
}
}
void addMessageToNotificationState(ApplicationDcContext dcContext, DcChat chat, int msgId) {
if (Prefs.isChatMuted(chat)) {
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);
}
synchronized (lock) {
notificationState.addNotification(new NotificationItem(id, chatRecipient, individualRecipient, chat.getId(), body, timestamp, slideDeck));
}
}
}
@@ -1,56 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}
@@ -1,54 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.content.Context;
import android.os.Build;
import org.thoughtcrime.securesms.util.Util;
public class MessageNotifierCompat {
public static final int NO_VISIBLE_CHAT_ID = -1;
static final int SUMMARY_NOTIFICATION_ID = 1339;
static final String EXTRA_REMOTE_REPLY = "extra_remote_reply";
private static MessageNotifier instance;
public static void init(Context context) {
if (instance != null) {
return;
}
if (Build.VERSION.SDK_INT < 23) {
instance = new MessageNotifierPreApi23(context);
} else {
instance = new MessageNotifierApi23(context);
}
}
public static void playSendSound() {
instance.playSendSound();
}
public static void updateNotification(int chatId, int messageId) {
Util.runOnAnyBackgroundThread(() -> instance.updateNotification(chatId, messageId));
}
public static void updateVisibleChat(int chatId) {
Util.runOnAnyBackgroundThread(() -> instance.updateVisibleChat(chatId));
}
public static void onNotificationPrivacyChanged() {
Util.runOnAnyBackgroundThread(() -> instance.onNotificationPrivacyChanged());
}
static void removeNotifications(int[] chatIds) {
Util.runOnAnyBackgroundThread(() -> instance.removeNotifications(chatIds));
}
public static void removeNotifications(int chatId) {
Util.runOnAnyBackgroundThread(() -> instance.removeNotifications(chatId));
}
}
@@ -1,82 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.content.Context;
import androidx.core.app.NotificationManagerCompat;
import com.b44t.messenger.DcChat;
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
import org.thoughtcrime.securesms.connect.DcHelper;
import java.util.List;
import static org.thoughtcrime.securesms.notifications.MessageNotifierCompat.SUMMARY_NOTIFICATION_ID;
class MessageNotifierPreApi23 extends MessageNotifier {
MessageNotifierPreApi23(Context context) {
super(context);
}
@Override
public void removeNotifications(int chatId) {
synchronized (lock) {
notificationState.removeNotificationsForChat(chatId);
}
recreateSummaryNotification();
}
@Override
public void removeNotifications(int[] chatIds) {
synchronized (lock) {
for (int id : chatIds) {
notificationState.removeNotificationsForChat(id);
}
}
recreateSummaryNotification();
}
@Override
void cancelNotifications(List<NotificationItem> removedItems) {
cancelNotifications();
}
@Override
void sendNotifications(DcChat chat, int messageId, boolean signal) {
ApplicationDcContext dcContext = DcHelper.getContext(appContext);
if (signal = isSignalAllowed(signal)) {
lastAudibleNotification = System.currentTimeMillis();
}
if (chat.isDeviceTalk()) {
// currently, we just never notify on device chat.
// esp. on first start, this is annoying.
return;
}
addMessageToNotificationState(dcContext, chat, messageId);
synchronized (lock) {
if (notificationState.hasMultipleChats()) {
sendMultipleChatNotification(appContext, notificationState, signal);
} else {
sendSingleChatNotification(appContext, notificationState, signal, false);
}
}
}
private void cancelNotifications() {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(appContext);
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID);
}
private void recreateSummaryNotification() {
cancelNotifications();
synchronized (lock) {
if (notificationState.hasMultipleChats()) {
sendMultipleChatNotification(appContext, notificationState, false);
} else {
sendSingleChatNotification(appContext, notificationState, false, false);
}
}
}
}
@@ -1,147 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class MultipleRecipientNotificationBuilder extends AbstractNotificationBuilder {
private final LinkedList<MessageBody> messageBodies = new LinkedList<>();
private static class MessageBody {
final @Nullable Recipient group;
final @NonNull Recipient sender;
final @NonNull CharSequence message;
MessageBody(@Nullable Recipient group, @NonNull Recipient sender, @NonNull CharSequence message) {
this.group = group;
this.sender = sender;
this.message = message;
}
}
MultipleRecipientNotificationBuilder(Context context, NotificationPrivacyPreference privacy) {
super(context, privacy);
setColor(context.getResources().getColor(R.color.delta_primary));
setSmallIcon(R.drawable.icon_notification);
setContentTitle(context.getString(R.string.app_name));
setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0));
setCategory(NotificationCompat.CATEGORY_MESSAGE);
setPriority(Prefs.getNotificationPriority(context));
setGroupSummary(true);
}
void setMessageCount(int messageCount, int chatCount) {
setSubText(context.getString(R.string.notify_n_messages_in_m_chats,
messageCount, chatCount));
setContentInfo(String.valueOf(messageCount));
setNumber(messageCount); // this also sets badges on android8 and newer
}
void setMostRecentSender(Recipient recipient) {
if (privacy.isDisplayContact()) {
setContentText(context.getString(R.string.notify_most_recent_from,
recipient.toShortString()));
}
}
void addActions(PendingIntent markAsReadIntent) {
NotificationCompat.Action markAllAsReadAction = new NotificationCompat.Action(R.drawable.check,
context.getString(R.string.notify_mark_all_read),
markAsReadIntent);
addAction(markAllAsReadAction);
extend(new NotificationCompat.WearableExtender().addAction(markAllAsReadAction));
}
void addMessageBody(@Nullable Recipient group, @NonNull Recipient sender, @Nullable CharSequence body) {
messageBodies.add(new MessageBody(group, sender, body));
if (privacy.isDisplayContact() && sender.getContactUri() != null) {
addPerson(sender.getContactUri().toString());
} else if (privacy.isDisplayContact() && group != null && group.getContactUri() != null) {
addPerson(group.getContactUri().toString());
}
}
@Override
public Notification build() {
if (privacy.isDisplayMessage() || privacy.isDisplayContact()) {
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
Map<Recipient, List<MessageBody>> byGroup = new LinkedHashMap<>();
for (MessageBody body : messageBodies) {
Recipient key = body.group == null ? body.sender : body.group;
if(byGroup.containsKey(key)) {
LinkedList<MessageBody> messagebodies = (LinkedList<MessageBody>) byGroup.remove(key);
messagebodies.addFirst(body);
byGroup.put(key, messagebodies);
} else {
byGroup.put(key, new LinkedList<>());
byGroup.get(key).add(body);
}
}
if (privacy.isDisplayMessage()) {
LinkedList<Recipient> list = new LinkedList<>(byGroup.keySet());
Iterator<Recipient> iterator = list.descendingIterator();
while(iterator.hasNext()) {
Recipient nextGroup = iterator.next();
String groupName = nextGroup.getName();
List<MessageBody> messages = byGroup.get(nextGroup);
String firstMessageSender = messages.get(0).sender.getName();
if(groupName != null && groupName.equals(firstMessageSender)) { // individual
for (MessageBody body : messages) {
style.addLine(getStyledMessage(body.sender, body.message));
}
} else { // group chat
style.addLine(Util.getBoldedString(groupName));
for (MessageBody body : messages) {
style.addLine("- " + getStyledMessage(body.sender, body.message));
}
}
}
} else if (privacy.isDisplayContact()) {
LinkedList<Recipient> list = new LinkedList<>(byGroup.keySet());
Iterator<Recipient> iterator = list.descendingIterator();
while(iterator.hasNext()) {
Recipient nextGroup = iterator.next();
String groupName = nextGroup.getName();
List<MessageBody> messages = byGroup.get(nextGroup);
String firstMessageSender = messages.get(0).sender.getName();
if(groupName != null && groupName.equals(firstMessageSender)) { // individual
for (MessageBody body : messages) {
style.addLine(body.sender.getName());
}
} else { // group chat
style.addLine(Util.getBoldedString(groupName));
for (MessageBody body : messages) {
style.addLine("- " + body.sender.getName());
}
}
}
}
setStyle(style);
}
return super.build();
}
}
@@ -1,87 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.TaskStackBuilder;
import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
public class NotificationItem {
private final int id;
private final @NonNull Recipient threadRecipient;
private final @NonNull Recipient individualRecipient;
private final int chatId;
private final @Nullable CharSequence text;
private final long timestamp;
private final @Nullable SlideDeck slideDeck;
NotificationItem(int id,
@NonNull Recipient threadRecipient,
@NonNull Recipient individualRecipient,
int chatId, @Nullable CharSequence text, long timestamp,
@Nullable SlideDeck slideDeck)
{
this.id = id;
this.threadRecipient = threadRecipient;
this.individualRecipient = individualRecipient;
this.text = text;
this.chatId = chatId;
this.timestamp = timestamp;
this.slideDeck = slideDeck;
}
public @NonNull Recipient getRecipient() {
return threadRecipient;
}
public @Nullable CharSequence getText() {
return text;
}
public @NonNull CharSequence getText(@NonNull CharSequence defaul) {
return (text == null ? defaul : text);
}
public long getTimestamp() {
return timestamp;
}
public int getChatId() {
return chatId;
}
@NonNull Recipient getIndividualRecipient() {
return individualRecipient;
}
@Deprecated
public int getThreadId() {
return chatId;
}
@Nullable SlideDeck getSlideDeck() {
return slideDeck;
}
@NonNull PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, ConversationActivity.class);
intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
return TaskStackBuilder.create(context)
.addNextIntentWithParentStack(intent)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
public int getId() {
return id;
}
}
@@ -1,142 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Prefs.VibrateState;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import static org.thoughtcrime.securesms.notifications.MessageNotifierCompat.SUMMARY_NOTIFICATION_ID;
public class NotificationState {
private final LinkedList<NotificationItem> notifications = new LinkedList<>();
private final LinkedHashSet<Integer> chats = new LinkedHashSet<>();
private int notificationCount = 0;
NotificationState() {}
NotificationState(@NonNull List<NotificationItem> items) {
for (NotificationItem item : items) {
addNotification(item);
}
}
public void reset() {
notificationCount = 0;
notifications.clear();
chats.clear();
}
void addNotification(NotificationItem item) {
notifications.addFirst(item);
if (chats.contains(item.getChatId())) {
chats.remove(item.getChatId());
}
chats.add(item.getChatId());
notificationCount++;
}
@Nullable Uri getRingtone(Context context) {
if (!notifications.isEmpty()) {
Recipient recipient = notifications.iterator().next().getRecipient();
if (recipient.getAddress().isDcChat()) {
return Prefs.getChatRingtone(context, recipient.getAddress().getDcChatId());
}
}
return null;
}
VibrateState getVibrate(Context context) {
if (!notifications.isEmpty()) {
Recipient recipient = notifications.iterator().next().getRecipient();
if (recipient.getAddress().isDcChat()) {
return Prefs.getChatVibrate(context, recipient.getAddress().getDcChatId());
}
}
return VibrateState.DEFAULT;
}
boolean hasMultipleChats() {
return chats.size() > 1;
}
public LinkedHashSet<Integer> getChats() {
return chats;
}
int getChatCount() {
return chats.size();
}
int getMessageCount() {
return notificationCount;
}
public List<NotificationItem> getNotifications() {
return notifications;
}
List<NotificationItem> getNotificationsForChat(int chatId) {
LinkedList<NotificationItem> list = new LinkedList<>();
for (NotificationItem item : notifications) {
if (item.getChatId() == chatId) list.addFirst(item);
}
return list;
}
List<NotificationItem> removeNotificationsForChat(int chatId) {
LinkedList<NotificationItem> removedItems = new LinkedList<>();
chats.remove(chatId);
for (Iterator<NotificationItem> it = notifications.iterator(); it.hasNext();) {
NotificationItem item = it.next();
if (item.getChatId() == chatId) {
removedItems.add(item);
it.remove();
}
}
notificationCount -= removedItems.size();
return removedItems;
}
PendingIntent getMarkAsReadIntent(Context context, int chatId, int notificationId) {
int index = 0;
int[] chatArray;
if (notificationId == SUMMARY_NOTIFICATION_ID) {
chatArray = new int[chats.size()];
for (int chat : chats) {
Log.w("NotificationState", "Added chat: " + chat);
chatArray[index++] = chat;
}
} else {
chatArray = new int[]{chatId};
}
Intent intent = new Intent(MarkReadReceiver.CLEAR_ACTION);
intent.setClass(context, MarkReadReceiver.class);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
//intent.putExtra(MarkReadReceiver.CHAT_IDS_EXTRA, chatArray);
//intent.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, notificationId);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
@@ -1,250 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationCompat.Action;
import androidx.core.app.RemoteInput;
import android.text.SpannableStringBuilder;
import android.util.Log;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class SingleRecipientNotificationBuilder extends AbstractNotificationBuilder {
private static final String TAG = SingleRecipientNotificationBuilder.class.getSimpleName();
private final LinkedList<CharSequence> messageBodies = new LinkedList<>();
private SlideDeck slideDeck;
private CharSequence contentTitle;
private CharSequence contentText;
SingleRecipientNotificationBuilder(@NonNull Context context, @NonNull NotificationPrivacyPreference privacy)
{
super(context, privacy);
setSmallIcon(R.drawable.icon_notification);
setColor(context.getResources().getColor(R.color.delta_primary));
setPriority(Prefs.getNotificationPriority(context));
setCategory(NotificationCompat.CATEGORY_MESSAGE);
}
public void setChat(@NonNull Recipient recipient) {
if (privacy.isDisplayContact()) {
setContentTitle(recipient.toShortString());
if (recipient.getContactUri() != null) {
addPerson(recipient.getContactUri().toString());
}
ContactPhoto contactPhoto = recipient.getContactPhoto(context);
FallbackContactPhoto fallbackContactPhoto = recipient.getFallbackContactPhoto();
if (contactPhoto != null) {
try {
setLargeIcon(GlideApp.with(context.getApplicationContext())
.load(contactPhoto)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.circleCrop()
.submit(context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height))
.get());
} catch (Exception e) {
Log.w(TAG, e);
setLargeIcon(fallbackContactPhoto.asDrawable(context, recipient.getFallbackAvatarColor(context)));
}
} else {
setLargeIcon(fallbackContactPhoto.asDrawable(context, recipient.getFallbackAvatarColor(context)));
}
} else {
setContentTitle(context.getString(R.string.app_name));
setLargeIcon(new GeneratedContactPhoto("Unknown").asDrawable(context, ThemeUtil.getDummyContactColor(context)));
}
}
public void setMessageCount(int messageCount) {
setContentInfo(String.valueOf(messageCount));
setNumber(messageCount); // this also sets badges on android8 and newer
}
public void setPrimaryMessageBody(@NonNull Recipient chatRecipients,
@NonNull Recipient individualRecipient,
@NonNull CharSequence message,
@Nullable SlideDeck slideDeck)
{
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && chatRecipients.isGroupRecipient()) {
stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString() + ": "));
}
if (privacy.isDisplayMessage()) {
setContentText(stringBuilder.append(message));
this.slideDeck = slideDeck;
} else {
setContentText(stringBuilder.append(context.getString(R.string.notify_new_message)));
}
}
void addActions(@NonNull PendingIntent markReadIntent,
@NonNull PendingIntent inNotificationReplyIntent)
{
Action markAsReadAction = new Action(R.drawable.check,
context.getString(R.string.notify_mark_read),
markReadIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Action replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp,
context.getString(R.string.notify_reply_button),
inNotificationReplyIntent)
.addRemoteInput(new RemoteInput.Builder(MessageNotifierCompat.EXTRA_REMOTE_REPLY)
.setLabel(context.getString(R.string.notify_reply_button)).build())
.build();
addAction(replyAction);
}
Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply,
context.getString(R.string.notify_reply_button),
inNotificationReplyIntent)
.addRemoteInput(new RemoteInput.Builder(MessageNotifierCompat.EXTRA_REMOTE_REPLY)
.setLabel(context.getString(R.string.notify_reply_button)).build())
.build();
addAction(markAsReadAction);
extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction)
.addAction(wearableReplyAction));
}
void addMessageBody(@NonNull Recipient chatRecipient,
@NonNull Recipient individualRecipient,
@Nullable CharSequence messageBody)
{
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && chatRecipient.isGroupRecipient()) {
stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString() + ": "));
}
if (privacy.isDisplayMessage()) {
messageBodies.addFirst(stringBuilder.append(messageBody == null ? "" : messageBody));
} else {
messageBodies.addFirst(stringBuilder.append(context.getString(R.string.notify_new_message)));
}
}
@Override
public Notification build() {
// the filtering whether or not to display messages and contacts is done in addMessageBody
// and setPrimaryMessageBody, no need to do it here again.
NotificationCompat.Style style;
if (Build.VERSION.SDK_INT < 23) {
style = new NotificationCompat.InboxStyle();
for (CharSequence messageBody : messageBodies) {
((NotificationCompat.InboxStyle) style).addLine(messageBody);
}
} else if (messageBodies.size() == 1 && hasBigPictureSlide(slideDeck)) {
style = new NotificationCompat.BigPictureStyle()
.bigPicture(getBigPicture(slideDeck))
.setSummaryText(getBigText(messageBodies));
} else {
style = new NotificationCompat.BigTextStyle().bigText(getBigText(messageBodies));
}
setStyle(style);
return super.build();
}
private void setLargeIcon(@Nullable Drawable drawable) {
if (drawable != null) {
int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
Bitmap recipientPhotoBitmap = BitmapUtil.createFromDrawable(drawable, largeIconTargetSize, largeIconTargetSize);
if (recipientPhotoBitmap != null) {
setLargeIcon(recipientPhotoBitmap);
}
}
}
private boolean hasBigPictureSlide(@Nullable SlideDeck slideDeck) {
if (slideDeck == null || Build.VERSION.SDK_INT < 16) {
return false;
}
Slide thumbnailSlide = slideDeck.getThumbnailSlide();
return thumbnailSlide != null &&
thumbnailSlide.hasImage() &&
thumbnailSlide.getThumbnailUri() != null;
}
private Bitmap getBigPicture(@NonNull SlideDeck slideDeck)
{
try {
@SuppressWarnings("ConstantConditions")
Uri uri = slideDeck.getThumbnailSlide().getThumbnailUri();
return GlideApp.with(context.getApplicationContext())
.asBitmap()
.load(new DecryptableStreamUriLoader.DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.submit(500, 500)
.get();
} catch (InterruptedException | ExecutionException e) {
Log.w(TAG, e);
return Bitmap.createBitmap(500, 500, Bitmap.Config.RGB_565);
}
}
@Override
public NotificationCompat.Builder setContentTitle(CharSequence contentTitle) {
this.contentTitle = contentTitle;
return super.setContentTitle(contentTitle);
}
public NotificationCompat.Builder setContentText(CharSequence contentText) {
this.contentText = contentText;
return super.setContentText(contentText);
}
private CharSequence getBigText(List<CharSequence> messageBodies) {
SpannableStringBuilder content = new SpannableStringBuilder();
for (CharSequence message : messageBodies) {
content.append(message);
content.append('\n');
}
return content;
}
}