From 7cc60e61735b927f4520ef54860b869be03b5338 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 26 Jun 2020 16:17:15 +1000 Subject: [PATCH 1/4] add flag in public chat poller for catching up --- .../loki/api/LokiPublicChatManager.kt | 18 ++++++++++++++++++ .../securesms/loki/api/LokiPublicChatPoller.kt | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt index 29b6feac52..93f1bfc7a1 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt @@ -20,6 +20,24 @@ class LokiPublicChatManager(private val context: Context) { private val observers = mutableMapOf() private var isPolling = false + public fun isAllCatchUp():Boolean { + var isAllCatchUp = true + refreshChatsAndPollers() + for ((threadId, chat) in chats) { + val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat) + isAllCatchUp = isAllCatchUp() && poller.isCatchUp() + } + return isAllCatchUp + } + + public fun shouldAllCatchUp() { + refreshChatsAndPollers() + for ((threadId, chat) in chats) { + val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat) + poller.shouldCatchUp() + } + } + public fun startPollersIfNeeded() { refreshChatsAndPollers() diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt index abefd33c40..0fed922b7c 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt @@ -35,6 +35,8 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki private val handler = Handler() private var hasStarted = false + private var isCatchUp = false + // region Convenience private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) private var displayNameUpdatees = setOf() @@ -82,6 +84,14 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki } // endregion + fun isCatchUp(): Boolean { + return isCatchUp + } + + fun shouldCatchUp() { + isCatchUp = false + } + // region Settings companion object { private val pollForNewMessagesInterval: Long = 4 * 1000 @@ -249,6 +259,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki processIncomingMessage(message) } } + isCatchUp = true }.fail { Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.") } From 871d70746004beb897fa02aeee918a2507832b19 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 26 Jun 2020 16:17:53 +1000 Subject: [PATCH 2/4] add optimized message notifier --- .../notifications/DefaultMessageNotifier.java | 621 ++++++++++++++++++ .../notifications/MessageNotifier.java | 620 +---------------- .../OptimizedMessageNotifier.java | 108 +++ 3 files changed, 741 insertions(+), 608 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java create mode 100644 src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java diff --git a/src/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java new file mode 100644 index 0000000000..c5b789eff6 --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -0,0 +1,621 @@ +/* + * 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.SuppressLint; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.service.notification.StatusBarNotification; +import android.support.annotation.NonNull; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactUtil; +import org.thoughtcrime.securesms.conversation.ConversationActivity; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.IncomingMessageObserver; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.SpanUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; +import org.whispersystems.signalservice.internal.util.Util; + +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import me.leolin.shortcutbadger.ShortcutBadger; +import network.loki.messenger.R; + +/** + * Handles posting system notifications for new messages. + * + * + * @author Moxie Marlinspike + */ + +public class DefaultMessageNotifier implements MessageNotifier { + + private static final String TAG = DefaultMessageNotifier.class.getSimpleName(); + + public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply"; + + private static final int SUMMARY_NOTIFICATION_ID = 1338; + private static final int PENDING_MESSAGES_ID = 1111; + private static final String NOTIFICATION_GROUP = "messages"; + private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2); + private static final long DESKTOP_ACTIVITY_PERIOD = TimeUnit.MINUTES.toMillis(1); + + private volatile static long visibleThread = -1; + private volatile static long lastDesktopActivityTimestamp = -1; + private volatile static long lastAudibleNotification = -1; + private static final CancelableExecutor executor = new CancelableExecutor(); + + @Override + public void setVisibleThread(long threadId) { + visibleThread = threadId; + } + + @Override + public void setLastDesktopActivityTimestamp(long timestamp) { + lastDesktopActivityTimestamp = timestamp; + } + + @Override + public void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { + if (visibleThread == threadId) { + sendInThreadNotification(context, recipient); + } else { + Intent intent = new Intent(context, ConversationActivity.class); + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); + intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); + + FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent); + ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) + .notify((int)threadId, builder.build()); + } + } + + public void notifyMessagesPending(Context context) { + if (!TextSecurePreferences.isNotificationsEnabled(context)) { + return; + } + + PendingMessageNotificationBuilder builder = new PendingMessageNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + ServiceUtil.getNotificationManager(context).notify(PENDING_MESSAGES_ID, builder.build()); + } + + @Override + public void cancelDelayedNotifications() { + executor.cancel(); + } + + private void cancelActiveNotifications(@NonNull Context context) { + NotificationManager notifications = ServiceUtil.getNotificationManager(context); + notifications.cancel(SUMMARY_NOTIFICATION_ID); + + if (Build.VERSION.SDK_INT >= 23) { + try { + StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); + + for (StatusBarNotification activeNotification : activeNotifications) { + if (activeNotification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION) { + notifications.cancel(activeNotification.getId()); + } + } + } catch (Throwable e) { + // XXX Appears to be a ROM bug, see #6043 + Log.w(TAG, e); + notifications.cancelAll(); + } + } + } + + private void cancelOrphanedNotifications(@NonNull Context context, NotificationState notificationState) { + if (Build.VERSION.SDK_INT >= 23) { + try { + NotificationManager notifications = ServiceUtil.getNotificationManager(context); + StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); + + for (StatusBarNotification notification : activeNotifications) { + boolean validNotification = false; + + if (notification.getId() != SUMMARY_NOTIFICATION_ID && + notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION && + notification.getId() != KeyCachingService.SERVICE_RUNNING_ID && + notification.getId() != IncomingMessageObserver.FOREGROUND_ID && + notification.getId() != PENDING_MESSAGES_ID) + { + for (NotificationItem item : notificationState.getNotifications()) { + if (notification.getId() == (SUMMARY_NOTIFICATION_ID + item.getThreadId())) { + validNotification = true; + break; + } + } + + if (!validNotification) { + notifications.cancel(notification.getId()); + } + } + } + } catch (Throwable e) { + // XXX Android ROM Bug, see #6043 + Log.w(TAG, e); + } + } + } + + @Override + public void updateNotification(@NonNull Context context) { + if (!TextSecurePreferences.isNotificationsEnabled(context)) { + return; + } + + updateNotification(context, false, 0); + } + + @Override + public void updateNotification(@NonNull Context context, long threadId) + { + if (System.currentTimeMillis() - lastDesktopActivityTimestamp < DESKTOP_ACTIVITY_PERIOD) { + Log.i(TAG, "Scheduling delayed notification..."); + executor.execute(new DelayedNotification(context, threadId)); + } else { + updateNotification(context, threadId, true); + } + } + + @Override + public void updateNotification(@NonNull Context context, long threadId, boolean signal) + { + boolean isVisible = visibleThread == threadId; + + ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context); + Recipient recipients = DatabaseFactory.getThreadDatabase(context) + .getRecipientForThreadId(threadId); + + if (isVisible) { + List messageIds = threads.setRead(threadId, false); + MarkReadReceiver.process(context, messageIds); + } + + if (!TextSecurePreferences.isNotificationsEnabled(context) || + (recipients != null && recipients.isMuted())) + { + return; + } + + if (isVisible) { + sendInThreadNotification(context, threads.getRecipientForThreadId(threadId)); + } else { + updateNotification(context, signal, 0); + } + } + + @Override + public void updateNotification(@NonNull Context context, boolean signal, int reminderCount) + { + Cursor telcoCursor = null; + Cursor pushCursor = null; + + try { + telcoCursor = DatabaseFactory.getMmsSmsDatabase(context).getUnread(); + pushCursor = DatabaseFactory.getPushDatabase(context).getPending(); + + if ((telcoCursor == null || telcoCursor.isAfterLast()) && + (pushCursor == null || pushCursor.isAfterLast())) + { + cancelActiveNotifications(context); + updateBadge(context, 0); + clearReminder(context); + return; + } + + NotificationState notificationState = constructNotificationState(context, telcoCursor); + + if (signal && (System.currentTimeMillis() - lastAudibleNotification) < MIN_AUDIBLE_PERIOD_MILLIS) { + signal = false; + } else if (signal) { + lastAudibleNotification = System.currentTimeMillis(); + } + + if (notificationState.hasMultipleThreads()) { + if (Build.VERSION.SDK_INT >= 23) { + for (long threadId : notificationState.getThreads()) { + sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true); + } + } + + sendMultipleThreadNotification(context, notificationState, signal); + } else { + sendSingleThreadNotification(context, notificationState, signal, false); + } + + cancelOrphanedNotifications(context, notificationState); + updateBadge(context, notificationState.getMessageCount()); + + if (signal) { + scheduleReminder(context, reminderCount); + } + } finally { + if (telcoCursor != null) telcoCursor.close(); + if (pushCursor != null) pushCursor.close(); + } + } + + private void sendSingleThreadNotification(@NonNull Context context, + @NonNull NotificationState notificationState, + boolean signal, boolean bundled) + { + Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled); + + if (notificationState.getNotifications().isEmpty()) { + if (!bundled) cancelActiveNotifications(context); + Log.i(TAG, "Empty notification state. Skipping."); + return; + } + + SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + List notifications = notificationState.getNotifications(); + Recipient recipient = notifications.get(0).getRecipient(); + int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0)); + + + builder.setThread(notifications.get(0).getRecipient()); + builder.setMessageCount(notificationState.getMessageCount()); + builder.setPrimaryMessageBody(recipient, notifications.get(0).getIndividualRecipient(), + notifications.get(0).getText(), notifications.get(0).getSlideDeck()); + builder.setContentIntent(notifications.get(0).getPendingIntent(context)); + builder.setDeleteIntent(notificationState.getDeleteIntent(context)); + builder.setOnlyAlertOnce(!signal); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); + builder.setAutoCancel(true); + + long timestamp = notifications.get(0).getTimestamp(); + if (timestamp != 0) builder.setWhen(timestamp); + + long threadID = notifications.get(0).getThreadId(); + + ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient); + + boolean canReply = SessionMetaProtocol.canUserReplyToNotification(recipient, context); + + PendingIntent quickReplyIntent = canReply ? notificationState.getQuickReplyIntent(context, recipient) : null; + PendingIntent remoteReplyIntent = canReply ? notificationState.getRemoteReplyIntent(context, recipient, replyMethod) : null; + + builder.addActions(notificationState.getMarkAsReadIntent(context, notificationId), + quickReplyIntent, + remoteReplyIntent, + replyMethod); + + if (canReply) { + builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, recipient), + notificationState.getAndroidAutoHeardIntent(context, notificationId), + notifications.get(0).getTimestamp()); + } + + 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()); + builder.setTicker(notifications.get(0).getIndividualRecipient(), + notifications.get(0).getText()); + } + + if (bundled) { + builder.setGroup(NOTIFICATION_GROUP); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); + } + + Notification notification = builder.build(); + NotificationManagerCompat.from(context).notify(notificationId, notification); + Log.i(TAG, "Posted notification. " + notification.toString()); + } + + private void sendMultipleThreadNotification(@NonNull Context context, + @NonNull NotificationState notificationState, + boolean signal) + { + Log.i(TAG, "sendMultiThreadNotification() signal: " + signal); + + MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + List notifications = notificationState.getNotifications(); + + builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount()); + builder.setMostRecentSender(notifications.get(0).getIndividualRecipient(), notifications.get(0).getRecipient()); + builder.setGroup(NOTIFICATION_GROUP); + builder.setDeleteIntent(notificationState.getDeleteIntent(context)); + builder.setOnlyAlertOnce(!signal); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); + builder.setAutoCancel(true); + + long timestamp = notifications.get(0).getTimestamp(); + if (timestamp != 0) builder.setWhen(timestamp); + + builder.addActions(notificationState.getMarkAsReadIntent(context, SUMMARY_NOTIFICATION_ID)); + + ListIterator iterator = notifications.listIterator(notifications.size()); + + while(iterator.hasPrevious()) { + NotificationItem item = iterator.previous(); + builder.addMessageBody(item.getIndividualRecipient(), item.getRecipient(), item.getText()); + } + + if (signal) { + builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate()); + builder.setTicker(notifications.get(0).getIndividualRecipient(), + notifications.get(0).getText()); + } + + Notification notification = builder.build(); + NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build()); + Log.i(TAG, "Posted notification. " + notification.toString()); + } + + private void sendInThreadNotification(Context context, Recipient recipient) { + if (!TextSecurePreferences.isInThreadNotifications(context) || + ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL) + { + return; + } + + Uri uri = null; + if (recipient != null) { + uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient) : recipient.getMessageRingtone(); + } + + if (uri == null) { + uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context); + } + + if (uri.toString().isEmpty()) { + Log.d(TAG, "ringtone uri is empty"); + return; + } + + Ringtone ringtone = RingtoneManager.getRingtone(context, uri); + + if (ringtone == null) { + Log.w(TAG, "ringtone is null"); + return; + } + + if (Build.VERSION.SDK_INT >= 21) { + ringtone.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) + .build()); + } else { + ringtone.setStreamType(AudioManager.STREAM_NOTIFICATION); + } + + ringtone.play(); + } + + private NotificationState constructNotificationState(@NonNull Context context, + @NonNull Cursor cursor) + { + NotificationState notificationState = new NotificationState(); + MmsSmsDatabase.Reader reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor); + + MessageRecord record; + + while ((record = reader.getNext()) != null) { + long id = record.getId(); + boolean mms = record.isMms() || record.isMmsNotification(); + Recipient recipient = record.getIndividualRecipient(); + Recipient conversationRecipient = record.getRecipient(); + long threadId = record.getThreadId(); + CharSequence body = record.getDisplayBody(context); + Recipient threadRecipients = null; + SlideDeck slideDeck = null; + long timestamp = record.getTimestamp(); + + + if (threadId != -1) { + threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); + } + + if (KeyCachingService.isLocked(context)) { + body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message)); + } else if (record.isMms() && !((MmsMessageRecord) record).getSharedContacts().isEmpty()) { + Contact contact = ((MmsMessageRecord) record).getSharedContacts().get(0); + body = ContactUtil.getStringSummary(context, contact); + } else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) { + body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker)); + slideDeck = ((MmsMessageRecord) record).getSlideDeck(); + } else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { + body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message)); + slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); + } else if (record.isMms() && !record.isMmsNotification() && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { + String message = context.getString(R.string.MessageNotifier_media_message_with_text, body); + int italicLength = message.length() - body.length(); + body = SpanUtil.italic(message, italicLength); + slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); + } + + if (threadRecipients == null || !threadRecipients.isMuted()) { + notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck)); + } + } + + reader.close(); + return notificationState; + } + + private void updateBadge(Context context, int count) { + try { + if (count == 0) ShortcutBadger.removeCount(context); + else ShortcutBadger.applyCount(context, count); + } catch (Throwable t) { + // NOTE :: I don't totally trust this thing, so I'm catching + // everything. + Log.w("MessageNotifier", t); + } + } + + private void scheduleReminder(Context context, int count) { + if (count >= TextSecurePreferences.getRepeatAlertsCount(context)) { + return; + } + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); + alarmIntent.putExtra("reminder_count", count); + + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + long timeout = TimeUnit.MINUTES.toMillis(2); + + alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); + } + + @Override + public void clearReminder(Context context) { + Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmManager.cancel(pendingIntent); + } + + public static class ReminderReceiver extends BroadcastReceiver { + + public static final String REMINDER_ACTION = "network.loki.securesms.MessageNotifier.REMINDER_ACTION"; + + @SuppressLint("StaticFieldLeak") + @Override + public void onReceive(final Context context, final Intent intent) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + int reminderCount = intent.getIntExtra("reminder_count", 0); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, true, reminderCount + 1); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private static class DelayedNotification implements Runnable { + + private static final long DELAY = TimeUnit.SECONDS.toMillis(5); + + private final AtomicBoolean canceled = new AtomicBoolean(false); + + private final Context context; + private final long threadId; + private final long delayUntil; + + private DelayedNotification(Context context, long threadId) { + this.context = context; + this.threadId = threadId; + this.delayUntil = System.currentTimeMillis() + DELAY; + } + + @Override + public void run() { + long delayMillis = delayUntil - System.currentTimeMillis(); + Log.i(TAG, "Waiting to notify: " + delayMillis); + + if (delayMillis > 0) { + Util.sleep(delayMillis); + } + + if (!canceled.get()) { + Log.i(TAG, "Not canceled, notifying..."); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, threadId, true); + ApplicationContext.getInstance(context).messageNotifier.cancelDelayedNotifications(); + } else { + Log.w(TAG, "Canceled, not notifying..."); + } + } + + public void cancel() { + canceled.set(true); + } + } + + private static class CancelableExecutor { + + private final Executor executor = Executors.newSingleThreadExecutor(); + private final Set tasks = new HashSet<>(); + + public void execute(final DelayedNotification runnable) { + synchronized (tasks) { + tasks.add(runnable); + } + + Runnable wrapper = new Runnable() { + @Override + public void run() { + runnable.run(); + + synchronized (tasks) { + tasks.remove(runnable); + } + } + }; + + executor.execute(wrapper); + } + + public void cancel() { + synchronized (tasks) { + for (DelayedNotification task : tasks) { + task.cancel(); + } + } + } + } +} diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java index c3fd7ad1e3..16f4343bea 100644 --- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -1,615 +1,19 @@ -/* - * 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.SuppressLint; -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.media.AudioAttributes; -import android.media.AudioManager; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.service.notification.StatusBarNotification; -import android.support.annotation.NonNull; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationManagerCompat; -import android.text.TextUtils; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactUtil; -import org.thoughtcrime.securesms.conversation.ConversationActivity; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; -import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.IncomingMessageObserver; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.SpanUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; -import org.whispersystems.signalservice.internal.util.Util; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import me.leolin.shortcutbadger.ShortcutBadger; -import network.loki.messenger.R; - -/** - * Handles posting system notifications for new messages. - * - * - * @author Moxie Marlinspike - */ - -public class MessageNotifier { - - private static final String TAG = MessageNotifier.class.getSimpleName(); - - public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply"; - - private static final int SUMMARY_NOTIFICATION_ID = 1338; - private static final int PENDING_MESSAGES_ID = 1111; - private static final String NOTIFICATION_GROUP = "messages"; - private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2); - private static final long DESKTOP_ACTIVITY_PERIOD = TimeUnit.MINUTES.toMillis(1); - - private volatile static long visibleThread = -1; - private volatile static long lastDesktopActivityTimestamp = -1; - private volatile static long lastAudibleNotification = -1; - private static final CancelableExecutor executor = new CancelableExecutor(); - - public static void setVisibleThread(long threadId) { - visibleThread = threadId; - } - - public static void setLastDesktopActivityTimestamp(long timestamp) { - lastDesktopActivityTimestamp = timestamp; - } - - public static void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { - if (visibleThread == threadId) { - sendInThreadNotification(context, recipient); - } else { - Intent intent = new Intent(context, ConversationActivity.class); - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); - intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); - - FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent); - ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) - .notify((int)threadId, builder.build()); - } - } - - public static void notifyMessagesPending(Context context) { - if (!TextSecurePreferences.isNotificationsEnabled(context)) { - return; - } - - PendingMessageNotificationBuilder builder = new PendingMessageNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); - ServiceUtil.getNotificationManager(context).notify(PENDING_MESSAGES_ID, builder.build()); - } - - public static void cancelDelayedNotifications() { - executor.cancel(); - } - - private static void cancelActiveNotifications(@NonNull Context context) { - NotificationManager notifications = ServiceUtil.getNotificationManager(context); - notifications.cancel(SUMMARY_NOTIFICATION_ID); - - if (Build.VERSION.SDK_INT >= 23) { - try { - StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); - - for (StatusBarNotification activeNotification : activeNotifications) { - if (activeNotification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION) { - notifications.cancel(activeNotification.getId()); - } - } - } catch (Throwable e) { - // XXX Appears to be a ROM bug, see #6043 - Log.w(TAG, e); - notifications.cancelAll(); - } - } - } - - private static void cancelOrphanedNotifications(@NonNull Context context, NotificationState notificationState) { - if (Build.VERSION.SDK_INT >= 23) { - try { - NotificationManager notifications = ServiceUtil.getNotificationManager(context); - StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); - - for (StatusBarNotification notification : activeNotifications) { - boolean validNotification = false; - - if (notification.getId() != SUMMARY_NOTIFICATION_ID && - notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION && - notification.getId() != KeyCachingService.SERVICE_RUNNING_ID && - notification.getId() != IncomingMessageObserver.FOREGROUND_ID && - notification.getId() != PENDING_MESSAGES_ID) - { - for (NotificationItem item : notificationState.getNotifications()) { - if (notification.getId() == (SUMMARY_NOTIFICATION_ID + item.getThreadId())) { - validNotification = true; - break; - } - } - - if (!validNotification) { - notifications.cancel(notification.getId()); - } - } - } - } catch (Throwable e) { - // XXX Android ROM Bug, see #6043 - Log.w(TAG, e); - } - } - } - - public static void updateNotification(@NonNull Context context) { - if (!TextSecurePreferences.isNotificationsEnabled(context)) { - return; - } - - updateNotification(context, false, 0); - } - - public static void updateNotification(@NonNull Context context, long threadId) - { - if (System.currentTimeMillis() - lastDesktopActivityTimestamp < DESKTOP_ACTIVITY_PERIOD) { - Log.i(TAG, "Scheduling delayed notification..."); - executor.execute(new DelayedNotification(context, threadId)); - } else { - updateNotification(context, threadId, true); - } - } - - public static void updateNotification(@NonNull Context context, - long threadId, - boolean signal) - { - boolean isVisible = visibleThread == threadId; - - ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context); - Recipient recipients = DatabaseFactory.getThreadDatabase(context) - .getRecipientForThreadId(threadId); - - if (isVisible) { - List messageIds = threads.setRead(threadId, false); - MarkReadReceiver.process(context, messageIds); - } - - if (!TextSecurePreferences.isNotificationsEnabled(context) || - (recipients != null && recipients.isMuted())) - { - return; - } - - if (isVisible) { - sendInThreadNotification(context, threads.getRecipientForThreadId(threadId)); - } else { - updateNotification(context, signal, 0); - } - } - - private static void updateNotification(@NonNull Context context, - boolean signal, - int reminderCount) - { - Cursor telcoCursor = null; - Cursor pushCursor = null; - - try { - telcoCursor = DatabaseFactory.getMmsSmsDatabase(context).getUnread(); - pushCursor = DatabaseFactory.getPushDatabase(context).getPending(); - - if ((telcoCursor == null || telcoCursor.isAfterLast()) && - (pushCursor == null || pushCursor.isAfterLast())) - { - cancelActiveNotifications(context); - updateBadge(context, 0); - clearReminder(context); - return; - } - - NotificationState notificationState = constructNotificationState(context, telcoCursor); - - if (signal && (System.currentTimeMillis() - lastAudibleNotification) < MIN_AUDIBLE_PERIOD_MILLIS) { - signal = false; - } else if (signal) { - lastAudibleNotification = System.currentTimeMillis(); - } - - if (notificationState.hasMultipleThreads()) { - if (Build.VERSION.SDK_INT >= 23) { - for (long threadId : notificationState.getThreads()) { - sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true); - } - } - - sendMultipleThreadNotification(context, notificationState, signal); - } else { - sendSingleThreadNotification(context, notificationState, signal, false); - } - - cancelOrphanedNotifications(context, notificationState); - updateBadge(context, notificationState.getMessageCount()); - - if (signal) { - scheduleReminder(context, reminderCount); - } - } finally { - if (telcoCursor != null) telcoCursor.close(); - if (pushCursor != null) pushCursor.close(); - } - } - - private static void sendSingleThreadNotification(@NonNull Context context, - @NonNull NotificationState notificationState, - boolean signal, boolean bundled) - { - Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled); - - if (notificationState.getNotifications().isEmpty()) { - if (!bundled) cancelActiveNotifications(context); - Log.i(TAG, "Empty notification state. Skipping."); - return; - } - - SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); - List notifications = notificationState.getNotifications(); - Recipient recipient = notifications.get(0).getRecipient(); - int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0)); - - - builder.setThread(notifications.get(0).getRecipient()); - builder.setMessageCount(notificationState.getMessageCount()); - builder.setPrimaryMessageBody(recipient, notifications.get(0).getIndividualRecipient(), - notifications.get(0).getText(), notifications.get(0).getSlideDeck()); - builder.setContentIntent(notifications.get(0).getPendingIntent(context)); - builder.setDeleteIntent(notificationState.getDeleteIntent(context)); - builder.setOnlyAlertOnce(!signal); - builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); - builder.setAutoCancel(true); - - long timestamp = notifications.get(0).getTimestamp(); - if (timestamp != 0) builder.setWhen(timestamp); - - long threadID = notifications.get(0).getThreadId(); - - ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient); - - boolean canReply = SessionMetaProtocol.canUserReplyToNotification(recipient, context); - - PendingIntent quickReplyIntent = canReply ? notificationState.getQuickReplyIntent(context, recipient) : null; - PendingIntent remoteReplyIntent = canReply ? notificationState.getRemoteReplyIntent(context, recipient, replyMethod) : null; - - builder.addActions(notificationState.getMarkAsReadIntent(context, notificationId), - quickReplyIntent, - remoteReplyIntent, - replyMethod); - - if (canReply) { - builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, recipient), - notificationState.getAndroidAutoHeardIntent(context, notificationId), - notifications.get(0).getTimestamp()); - } - - 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()); - builder.setTicker(notifications.get(0).getIndividualRecipient(), - notifications.get(0).getText()); - } - - if (bundled) { - builder.setGroup(NOTIFICATION_GROUP); - builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); - } - - Notification notification = builder.build(); - NotificationManagerCompat.from(context).notify(notificationId, notification); - Log.i(TAG, "Posted notification. " + notification.toString()); - } - - private static void sendMultipleThreadNotification(@NonNull Context context, - @NonNull NotificationState notificationState, - boolean signal) - { - Log.i(TAG, "sendMultiThreadNotification() signal: " + signal); - - MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); - List notifications = notificationState.getNotifications(); - - builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount()); - builder.setMostRecentSender(notifications.get(0).getIndividualRecipient(), notifications.get(0).getRecipient()); - builder.setGroup(NOTIFICATION_GROUP); - builder.setDeleteIntent(notificationState.getDeleteIntent(context)); - builder.setOnlyAlertOnce(!signal); - builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); - builder.setAutoCancel(true); - - long timestamp = notifications.get(0).getTimestamp(); - if (timestamp != 0) builder.setWhen(timestamp); - - builder.addActions(notificationState.getMarkAsReadIntent(context, SUMMARY_NOTIFICATION_ID)); - - ListIterator iterator = notifications.listIterator(notifications.size()); - - while(iterator.hasPrevious()) { - NotificationItem item = iterator.previous(); - builder.addMessageBody(item.getIndividualRecipient(), item.getRecipient(), item.getText()); - } - - if (signal) { - builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate()); - builder.setTicker(notifications.get(0).getIndividualRecipient(), - notifications.get(0).getText()); - } - - Notification notification = builder.build(); - NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build()); - Log.i(TAG, "Posted notification. " + notification.toString()); - } - - private static void sendInThreadNotification(Context context, Recipient recipient) { - if (!TextSecurePreferences.isInThreadNotifications(context) || - ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL) - { - return; - } - - Uri uri = null; - if (recipient != null) { - uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient) : recipient.getMessageRingtone(); - } - - if (uri == null) { - uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context); - } - - if (uri.toString().isEmpty()) { - Log.d(TAG, "ringtone uri is empty"); - return; - } - - Ringtone ringtone = RingtoneManager.getRingtone(context, uri); - - if (ringtone == null) { - Log.w(TAG, "ringtone is null"); - return; - } - - if (Build.VERSION.SDK_INT >= 21) { - ringtone.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) - .build()); - } else { - ringtone.setStreamType(AudioManager.STREAM_NOTIFICATION); - } - - ringtone.play(); - } - - private static NotificationState constructNotificationState(@NonNull Context context, - @NonNull Cursor cursor) - { - NotificationState notificationState = new NotificationState(); - MmsSmsDatabase.Reader reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor); - - MessageRecord record; - - while ((record = reader.getNext()) != null) { - long id = record.getId(); - boolean mms = record.isMms() || record.isMmsNotification(); - Recipient recipient = record.getIndividualRecipient(); - Recipient conversationRecipient = record.getRecipient(); - long threadId = record.getThreadId(); - CharSequence body = record.getDisplayBody(context); - Recipient threadRecipients = null; - SlideDeck slideDeck = null; - long timestamp = record.getTimestamp(); - - - if (threadId != -1) { - threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - } - - if (KeyCachingService.isLocked(context)) { - body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message)); - } else if (record.isMms() && !((MmsMessageRecord) record).getSharedContacts().isEmpty()) { - Contact contact = ((MmsMessageRecord) record).getSharedContacts().get(0); - body = ContactUtil.getStringSummary(context, contact); - } else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) { - body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker)); - slideDeck = ((MmsMessageRecord) record).getSlideDeck(); - } else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { - body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message)); - slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); - } else if (record.isMms() && !record.isMmsNotification() && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { - String message = context.getString(R.string.MessageNotifier_media_message_with_text, body); - int italicLength = message.length() - body.length(); - body = SpanUtil.italic(message, italicLength); - slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); - } - - if (threadRecipients == null || !threadRecipients.isMuted()) { - notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck)); - } - } - - reader.close(); - return notificationState; - } - - private static void updateBadge(Context context, int count) { - try { - if (count == 0) ShortcutBadger.removeCount(context); - else ShortcutBadger.applyCount(context, count); - } catch (Throwable t) { - // NOTE :: I don't totally trust this thing, so I'm catching - // everything. - Log.w("MessageNotifier", t); - } - } - - private static void scheduleReminder(Context context, int count) { - if (count >= TextSecurePreferences.getRepeatAlertsCount(context)) { - return; - } - - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); - alarmIntent.putExtra("reminder_count", count); - - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); - long timeout = TimeUnit.MINUTES.toMillis(2); - - alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); - } - - public static void clearReminder(Context context) { - Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarmManager.cancel(pendingIntent); - } - - public static class ReminderReceiver extends BroadcastReceiver { - - public static final String REMINDER_ACTION = "network.loki.securesms.MessageNotifier.REMINDER_ACTION"; - - @SuppressLint("StaticFieldLeak") - @Override - public void onReceive(final Context context, final Intent intent) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - int reminderCount = intent.getIntExtra("reminder_count", 0); - MessageNotifier.updateNotification(context, true, reminderCount + 1); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private static class DelayedNotification implements Runnable { - - private static final long DELAY = TimeUnit.SECONDS.toMillis(5); - - private final AtomicBoolean canceled = new AtomicBoolean(false); - - private final Context context; - private final long threadId; - private final long delayUntil; - - private DelayedNotification(Context context, long threadId) { - this.context = context; - this.threadId = threadId; - this.delayUntil = System.currentTimeMillis() + DELAY; - } - - @Override - public void run() { - long delayMillis = delayUntil - System.currentTimeMillis(); - Log.i(TAG, "Waiting to notify: " + delayMillis); - - if (delayMillis > 0) { - Util.sleep(delayMillis); - } - - if (!canceled.get()) { - Log.i(TAG, "Not canceled, notifying..."); - MessageNotifier.updateNotification(context, threadId, true); - MessageNotifier.cancelDelayedNotifications(); - } else { - Log.w(TAG, "Canceled, not notifying..."); - } - } - - public void cancel() { - canceled.set(true); - } - } - - private static class CancelableExecutor { - - private final Executor executor = Executors.newSingleThreadExecutor(); - private final Set tasks = new HashSet<>(); - - public void execute(final DelayedNotification runnable) { - synchronized (tasks) { - tasks.add(runnable); - } - - Runnable wrapper = new Runnable() { - @Override - public void run() { - runnable.run(); - - synchronized (tasks) { - tasks.remove(runnable); - } - } - }; - - executor.execute(wrapper); - } - - public void cancel() { - synchronized (tasks) { - for (DelayedNotification task : tasks) { - task.cancel(); - } - } - } - } +import androidx.annotation.NonNull; + +public interface MessageNotifier { + void setVisibleThread(long threadId); + void setLastDesktopActivityTimestamp(long timestamp); + void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId); + void cancelDelayedNotifications(); + void updateNotification(@NonNull Context context); + void updateNotification(@NonNull Context context, long threadId); + void updateNotification(@NonNull Context context, long threadId, boolean signal); + void updateNotification(@android.support.annotation.NonNull Context context, boolean signal, int reminderCount); + void clearReminder(@NonNull Context context); } diff --git a/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java new file mode 100644 index 0000000000..ed812d5a87 --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -0,0 +1,108 @@ +package org.thoughtcrime.securesms.notifications; + +import android.content.Context; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Debouncer; +import org.thoughtcrime.securesms.util.Throttler; +import org.whispersystems.signalservice.loki.api.LokiPoller; + +import java.util.concurrent.TimeUnit; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; + +public class OptimizedMessageNotifier implements MessageNotifier { + private final MessageNotifier wrapped; + private final Debouncer debouncer; + + @MainThread + public OptimizedMessageNotifier(@NonNull MessageNotifier wrapped) { + this.wrapped = wrapped; + this.debouncer = new Debouncer(TimeUnit.SECONDS.toMillis(1)); + } + + @Override + public void setVisibleThread(long threadId) { wrapped.setVisibleThread(threadId); } + + @Override + public void setLastDesktopActivityTimestamp(long timestamp) { wrapped.setLastDesktopActivityTimestamp(timestamp);} + + @Override + public void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { + wrapped.notifyMessageDeliveryFailed(context, recipient, threadId); + } + + @Override + public void cancelDelayedNotifications() { wrapped.cancelDelayedNotifications(); } + + @Override + public void updateNotification(@NonNull Context context) { + LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; + LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; + Boolean isCatchUp = false; + if (lokiPoller != null && lokiPublicChatManager != null) { + isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + } + + if (isCatchUp) { + wrapped.updateNotification(context); + } else { + debouncer.publish(() -> wrapped.updateNotification(context)); + } + } + + @Override + public void updateNotification(@NonNull Context context, long threadId) { + LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; + LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; + Boolean isCatchUp = false; + if (lokiPoller != null && lokiPublicChatManager != null) { + isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + } + + if (isCatchUp) { + wrapped.updateNotification(context, threadId); + } else { + debouncer.publish(() -> wrapped.updateNotification(context, threadId)); + } + } + + @Override + public void updateNotification(@NonNull Context context, long threadId, boolean signal) { + LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; + LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; + Boolean isCatchUp = false; + if (lokiPoller != null && lokiPublicChatManager != null) { + isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + } + + if (isCatchUp) { + wrapped.updateNotification(context, threadId, signal); + } else { + debouncer.publish(() -> wrapped.updateNotification(context, threadId, signal)); + } + } + + @Override + public void updateNotification(@android.support.annotation.NonNull Context context, boolean signal, int reminderCount) { + LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; + LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; + Boolean isCatchUp = false; + if (lokiPoller != null && lokiPublicChatManager != null) { + isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + } + + if (isCatchUp) { + wrapped.updateNotification(context, signal, reminderCount); + } else { + debouncer.publish(() -> wrapped.updateNotification(context, signal, reminderCount)); + } + } + + + @Override + public void clearReminder(@NonNull Context context) { wrapped.clearReminder(context); } +} From 2a88de3f610ff33b1397eacdbb666770b46c017f Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 26 Jun 2020 16:18:19 +1000 Subject: [PATCH 3/4] move old message notifier to optimized message notifier --- .../securesms/ApplicationContext.java | 8 +++++- .../securesms/ConversationListActivity.java | 2 +- .../securesms/ConversationListFragment.java | 17 ++++++----- .../securesms/DatabaseUpgradeActivity.java | 2 +- .../securesms/MessageDetailsActivity.java | 4 +-- .../conversation/ConversationActivity.java | 6 ++-- .../database/helpers/ClassicOpenHelper.java | 3 +- .../groups/GroupMessageProcessor.java | 2 +- .../securesms/jobs/AttachmentDownloadJob.java | 3 +- .../securesms/jobs/MmsDownloadJob.java | 11 +++++--- .../securesms/jobs/MmsSendJob.java | 3 +- .../securesms/jobs/PushDecryptJob.java | 28 +++++++++++-------- .../securesms/jobs/PushSendJob.java | 2 +- .../securesms/jobs/PushTextSendJob.java | 4 +-- .../securesms/jobs/SmsReceiveJob.java | 3 +- .../securesms/jobs/SmsSendJob.java | 5 ++-- .../securesms/jobs/SmsSentJob.java | 2 +- .../securesms/loki/activities/HomeActivity.kt | 4 +-- .../AndroidAutoHeardReceiver.java | 3 +- .../AndroidAutoReplyReceiver.java | 3 +- .../DeleteNotificationReceiver.java | 3 +- .../notifications/MarkReadReceiver.java | 2 +- .../notifications/RemoteReplyReceiver.java | 5 ++-- .../SingleRecipientNotificationBuilder.java | 4 +-- .../NotificationsPreferenceFragment.java | 2 +- .../securesms/service/KeyCachingService.java | 4 +-- .../securesms/service/WebRtcCallService.java | 2 +- .../securesms/util/IdentityUtil.java | 3 +- 28 files changed, 83 insertions(+), 57 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 3121f02dd5..32f464b1f6 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -69,8 +69,10 @@ import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage; import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob; import org.thoughtcrime.securesms.loki.utilities.Broadcaster; +import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; @@ -148,6 +150,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc private PersistentLogger persistentLogger; // Loki + public MessageNotifier messageNotifier = null; public LokiPoller lokiPoller = null; public LokiPublicChatManager lokiPublicChatManager = null; private LokiPublicChatAPI lokiPublicChatAPI = null; @@ -173,6 +176,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc ProcessLifecycleOwner.get().getLifecycle().addObserver(this); // Loki // ======== + messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); broadcaster = new Broadcaster(this); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this); @@ -222,7 +226,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc executePendingContactSync(); KeyCachingService.onAppForegrounded(this); // Loki + if (lokiPoller != null) { lokiPoller.shouldCatchUp(); } startPollingIfNeeded(); + lokiPublicChatManager.shouldAllCatchUp(); lokiPublicChatManager.startPollersIfNeeded(); } @@ -231,7 +237,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc isAppVisible = false; Log.i(TAG, "App is no longer visible."); KeyCachingService.onAppBackgrounded(this); - MessageNotifier.setVisibleThread(-1); + messageNotifier.setVisibleThread(-1); // Loki if (lokiPoller != null) { lokiPoller.stopIfNeeded(); } if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); } diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 49fc125778..2d9828705d 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -298,7 +298,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit Context context = ConversationListActivity.this; List messageIds = DatabaseFactory.getThreadDatabase(context).setAllThreadsRead(); - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); MarkReadReceiver.process(context, messageIds); return null; diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java index d96d9860b7..27fb781e5a 100644 --- a/src/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java @@ -324,8 +324,9 @@ public class ConversationListFragment extends Fragment @Override protected Void doInBackground(Void... params) { - DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations); - MessageNotifier.updateNotification(getActivity()); + Context context = getActivity(); + DatabaseFactory.getThreadDatabase(context).deleteConversations(selectedConversations); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); return null; } @@ -542,9 +543,10 @@ public class ConversationListFragment extends Fragment DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); if (unreadCount > 0) { - List messageIds = DatabaseFactory.getThreadDatabase(getActivity()).setRead(threadId, false); - MessageNotifier.updateNotification(getActivity()); - MarkReadReceiver.process(getActivity(), messageIds); + Context context = getActivity(); + List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, false); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + MarkReadReceiver.process(context, messageIds); } } @@ -553,8 +555,9 @@ public class ConversationListFragment extends Fragment DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); if (unreadCount > 0) { - DatabaseFactory.getThreadDatabase(getActivity()).incrementUnread(threadId, unreadCount); - MessageNotifier.updateNotification(getActivity()); + Context context = getActivity(); + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, unreadCount); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); diff --git a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java index ad9bfcb85e..1e6f34a855 100644 --- a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java +++ b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java @@ -176,7 +176,7 @@ public class DatabaseUpgradeActivity extends BaseActivity { new AsyncTask() { @Override protected Void doInBackground(Void... params) { - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); diff --git a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java index 147299bbe0..818a3d6669 100644 --- a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java @@ -133,13 +133,13 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity assert getSupportActionBar() != null; getSupportActionBar().setTitle("Message Details"); - MessageNotifier.setVisibleThread(threadId); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); } @Override protected void onPause() { super.onPause(); - MessageNotifier.setVisibleThread(-1L); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L); } @Override diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index f85b0df933..a18509f92b 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -550,7 +550,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity setGroupShareProfileReminder(recipient); calculateCharactersRemaining(); - MessageNotifier.setVisibleThread(threadId); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); markThreadAsRead(); DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this); @@ -565,7 +565,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected void onPause() { super.onPause(); - MessageNotifier.setVisibleThread(-1L); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L); if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right); inputPanel.onPause(); @@ -2244,7 +2244,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (refreshFragment) { fragment.reload(recipient, threadId); - MessageNotifier.setVisibleThread(threadId); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); } fragment.scrollToBottom(); diff --git a/src/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java index 8eb29d3509..7c9fe9974e 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java @@ -12,6 +12,7 @@ import android.text.TextUtils; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.DatabaseUpgradeActivity; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; @@ -423,7 +424,7 @@ public class ClassicOpenHelper extends SQLiteOpenHelper { db.endTransaction(); // DecryptingQueue.schedulePendingDecrypts(context, masterSecret); - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); } @Override diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java index 5dacafdd65..6ccb3279d9 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java +++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java @@ -281,7 +281,7 @@ public class GroupMessageProcessor { Optional insertResult = smsDatabase.insertMessageInbox(groupMessage); if (insertResult.isPresent()) { - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); return insertResult.get().getThreadId(); } else { return null; diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java index 5b4b7809ca..5b34dc2f71 100644 --- a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java @@ -5,6 +5,7 @@ import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; @@ -107,7 +108,7 @@ public class AttachmentDownloadJob extends BaseJob implements InjectableType { @Override public void onRun() throws IOException { doWork(); - MessageNotifier.updateNotification(context, 0); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, 0); } public void doWork() throws IOException { diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 47411c08f0..5b1d81abcc 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -10,6 +10,7 @@ import com.google.android.mms.pdu_alt.PduBody; import com.google.android.mms.pdu_alt.PduPart; import com.google.android.mms.pdu_alt.RetrieveConf; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.database.Address; @@ -57,6 +58,7 @@ public class MmsDownloadJob extends BaseJob { private long messageId; private long threadId; private boolean automatic; + private MessageNotifier messageNotifier; public MmsDownloadJob(long messageId, long threadId, boolean automatic) { this(new Job.Parameters.Builder() @@ -75,6 +77,7 @@ public class MmsDownloadJob extends BaseJob { this.messageId = messageId; this.threadId = threadId; this.automatic = automatic; + this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier; } @Override @@ -94,7 +97,7 @@ public class MmsDownloadJob extends BaseJob { public void onAdded() { if (automatic && KeyCachingService.isLocked(context)) { DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId); - MessageNotifier.updateNotification(context); + messageNotifier.updateNotification(context); } } @@ -177,7 +180,7 @@ public class MmsDownloadJob extends BaseJob { if (automatic) { database.markIncomingNotificationReceived(threadId); - MessageNotifier.updateNotification(context, threadId); + messageNotifier.updateNotification(context, threadId); } } @@ -252,7 +255,7 @@ public class MmsDownloadJob extends BaseJob { if (insertResult.isPresent()) { database.delete(messageId); - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } @@ -264,7 +267,7 @@ public class MmsDownloadJob extends BaseJob { if (automatic) { db.markIncomingNotificationReceived(threadId); - MessageNotifier.updateNotification(context, threadId); + messageNotifier.updateNotification(context, threadId); } } diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index 315e13ad64..e3005573e3 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -19,6 +19,7 @@ import com.google.android.mms.pdu_alt.SendReq; import com.google.android.mms.smil.SmilHelper; import com.klinker.android.send_message.Utils; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -304,7 +305,7 @@ public class MmsSendJob extends SendJob { Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); if (recipient != null) { - MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 9f90b19ed0..44e29fd1c8 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -159,6 +159,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private long messageId; private long smsMessageId; + //Loki + private MessageNotifier messageNotifier; + @Inject SignalServiceMessageSender messageSender; private Address author; @@ -178,6 +181,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { pushMessageId, smsMessageId); setContext(context); + this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier; } private PushDecryptJob(@NonNull Job.Parameters parameters, long pushMessageId, long smsMessageId) { @@ -546,7 +550,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (threadId != null) { SessionManagementProtocol.handleEndSessionMessageIfNeeded(context, content); - MessageNotifier.updateNotification(context, threadId); + messageNotifier.updateNotification(context, threadId); } } @@ -709,10 +713,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (threadId != null) { DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); - MessageNotifier.updateNotification(context); + messageNotifier.updateNotification(context); } - MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp()); + messageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp()); } catch (MmsException e) { throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); } @@ -775,9 +779,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } } - MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); - MessageNotifier.cancelDelayedNotifications(); - MessageNotifier.updateNotification(context); + messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); + messageNotifier.cancelDelayedNotifications(); + messageNotifier.updateNotification(context); } public void handleMediaMessage(@NonNull SignalServiceContent content, @@ -843,7 +847,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } if (insertResult.isPresent()) { - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } // Loki - Store message open group server ID if needed @@ -1015,7 +1019,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); if (threadId != null) { - MessageNotifier.updateNotification(context, threadId); + messageNotifier.updateNotification(context, threadId); } if (insertResult.isPresent()) { @@ -1116,7 +1120,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (insertResult.isPresent()) { smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId()); - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } else { smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get()); @@ -1133,7 +1137,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (insertResult.isPresent()) { smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId()); - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } else { smsDatabase.markAsDecryptFailed(smsMessageId.get()); @@ -1151,7 +1155,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (insertResult.isPresent()) { smsDatabase.markAsNoSession(insertResult.get().getMessageId()); - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } else { smsDatabase.markAsNoSession(smsMessageId.get()); @@ -1169,7 +1173,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (insertResult.isPresent()) { smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId()); - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } else { smsDatabase.markAsLegacyVersion(smsMessageId.get()); diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index 3d0eb2e259..b3551572fe 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -182,7 +182,7 @@ public abstract class PushSendJob extends SendJob { Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); if (threadId != -1 && recipient != null) { - MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 3d018d14dc..fbb3a068b8 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -164,7 +164,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { warn(TAG, "Couldn't send message due to error: ", e); if (messageId >= 0) { database.markAsPendingInsecureSmsFallback(record.getId()); - MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); } } catch (UntrustedIdentityException e) { warn(TAG, "Couldn't send message due to error: ", e); @@ -198,7 +198,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); if (threadId != -1 && recipient != null) { - MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); } } } diff --git a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java index 69a6e5c12b..e6463eb11e 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.telephony.SmsMessage; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; @@ -79,7 +80,7 @@ public class SmsReceiveJob extends BaseJob { Optional insertResult = storeMessage(message.get()); if (insertResult.isPresent()) { - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } else if (message.isPresent()) { Log.w(TAG, "*** Received blocked SMS, ignoring..."); diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java index 282099393b..ed4ebcfdf4 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java @@ -9,6 +9,7 @@ import android.support.annotation.NonNull; import android.telephony.PhoneNumberUtils; import android.telephony.SmsManager; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint; @@ -88,7 +89,7 @@ public class SmsSendJob extends SendJob { } catch (UndeliverableMessageException ude) { warn(TAG, ude); DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId()); - MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); } } @@ -106,7 +107,7 @@ public class SmsSendJob extends SendJob { DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); if (threadId != -1 && recipient != null) { - MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); } } diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java index cadecd0b81..43fc8d4170 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java @@ -108,7 +108,7 @@ public class SmsSentJob extends BaseJob { break; default: database.markAsSentFailed(messageId); - MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); } } catch (NoSuchMessageException e) { Log.w(TAG, e); diff --git a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 5a4ab5d8bc..2921dc1108 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -78,7 +78,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe val threadID = archivedConversations.getLong(archivedConversations.getColumnIndex(ThreadDatabase.ID)) AsyncTask.execute { threadDatabase.deleteConversation(threadID) - MessageNotifier.updateNotification(this) + (applicationContext as ApplicationContext).messageNotifier.updateNotification(this) } } deleteThreadAtCurrentPosition() @@ -314,7 +314,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe ApplicationContext.getInstance(activity).lokiPublicChatAPI!!.leave(publicChat.channel, publicChat.server) } threadDatabase.deleteConversation(threadID) - MessageNotifier.updateNotification(activity) + ApplicationContext.getInstance(activity).messageNotifier.updateNotification(activity) } } } diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java index dfa974a1f8..c77e2753b7 100644 --- a/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.os.AsyncTask; import android.support.v4.app.NotificationManagerCompat; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.whispersystems.libsignal.logging.Log; @@ -66,7 +67,7 @@ public class AndroidAutoHeardReceiver extends BroadcastReceiver { messageIdsCollection.addAll(messageIds); } - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); MarkReadReceiver.process(context, messageIdsCollection); return null; diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java index d196fe432e..d55df50a33 100644 --- a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java @@ -25,6 +25,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.RemoteInput; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; @@ -86,7 +87,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver { List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId, true); - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); MarkReadReceiver.process(context, messageIds); return null; diff --git a/src/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java b/src/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java index ea065fd18a..ed2727f647 100644 --- a/src/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.Intent; import android.os.AsyncTask; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.DatabaseFactory; public class DeleteNotificationReceiver extends BroadcastReceiver { @@ -18,7 +19,7 @@ public class DeleteNotificationReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, Intent intent) { if (DELETE_NOTIFICATION_ACTION.equals(intent.getAction())) { - MessageNotifier.clearReminder(context); + ApplicationContext.getInstance(context).messageNotifier.clearReminder(context); final long[] ids = intent.getLongArrayExtra(EXTRA_IDS); final boolean[] mms = intent.getBooleanArrayExtra(EXTRA_MMS); diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java index 04aa6d8ead..6e5c32bf0f 100644 --- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -61,7 +61,7 @@ public class MarkReadReceiver extends BroadcastReceiver { process(context, messageIdsCollection); - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); return null; } diff --git a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index 7b42d10aca..84ff4c1664 100644 --- a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -25,6 +25,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.RemoteInput; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; @@ -59,7 +60,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver { final Address address = intent.getParcelableExtra(ADDRESS_EXTRA); final ReplyMethod replyMethod = (ReplyMethod) intent.getSerializableExtra(REPLY_METHOD); - final CharSequence responseText = remoteInput.getCharSequence(MessageNotifier.EXTRA_REMOTE_REPLY); + final CharSequence responseText = remoteInput.getCharSequence(DefaultMessageNotifier.EXTRA_REMOTE_REPLY); if (address == null) throw new AssertionError("No address specified"); if (replyMethod == null) throw new AssertionError("No reply method specified"); @@ -91,7 +92,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver { List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); - MessageNotifier.updateNotification(context); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); MarkReadReceiver.process(context, messageIds); return null; diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 7f8bf3abc9..0476cdff2f 100644 --- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -177,14 +177,14 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp, actionName, wearableReplyIntent) - .addRemoteInput(new RemoteInput.Builder(MessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build()) + .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build()) .build(); } Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, actionName, wearableReplyIntent) - .addRemoteInput(new RemoteInput.Builder(MessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build()) + .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build()) .build(); diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java index 9b21b90dad..b3ddb9cd1b 100644 --- a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java @@ -220,7 +220,7 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme new AsyncTask() { @Override protected Void doInBackground(Void... params) { - MessageNotifier.updateNotification(getActivity()); + ApplicationContext.getInstance(getActivity()).messageNotifier.updateNotification(getActivity()); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); diff --git a/src/org/thoughtcrime/securesms/service/KeyCachingService.java b/src/org/thoughtcrime/securesms/service/KeyCachingService.java index 796249b659..b07710e730 100644 --- a/src/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/src/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -115,7 +115,7 @@ public class KeyCachingService extends Service { @Override protected Void doInBackground(Void... params) { if (!DatabaseUpgradeActivity.isUpdate(KeyCachingService.this)) { - MessageNotifier.updateNotification(KeyCachingService.this); + ApplicationContext.getInstance(KeyCachingService.this).messageNotifier.updateNotification(KeyCachingService.this); } return null; } @@ -188,7 +188,7 @@ public class KeyCachingService extends Service { new AsyncTask() { @Override protected Void doInBackground(Void... params) { - MessageNotifier.updateNotification(KeyCachingService.this); + ApplicationContext.getInstance(KeyCachingService.this).messageNotifier.updateNotification(KeyCachingService.this); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); diff --git a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java index 0de3ac406f..855b327131 100644 --- a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -721,7 +721,7 @@ public class WebRtcCallService extends Service implements InjectableType, private void insertMissedCall(@NonNull Recipient recipient, boolean signal) { Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); - MessageNotifier.updateNotification(this, messageAndThreadId.second, signal); + ApplicationContext.getInstance(this).messageNotifier.updateNotification(this, messageAndThreadId.second, signal); } private void handleAnswerCall(Intent intent) { diff --git a/src/org/thoughtcrime/securesms/util/IdentityUtil.java b/src/org/thoughtcrime/securesms/util/IdentityUtil.java index ae712c9f71..fe5542e9a5 100644 --- a/src/org/thoughtcrime/securesms/util/IdentityUtil.java +++ b/src/org/thoughtcrime/securesms/util/IdentityUtil.java @@ -6,6 +6,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.database.Address; @@ -143,7 +144,7 @@ public class IdentityUtil { Optional insertResult = smsDatabase.insertMessageInbox(individualUpdate); if (insertResult.isPresent()) { - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); } } From d09171213d18c42db8b45ef687be4a50a37c03a5 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 8 Jul 2020 11:30:00 +1000 Subject: [PATCH 4/4] Clean --- .../securesms/ApplicationContext.java | 4 +-- .../securesms/jobs/PushDecryptJob.java | 1 - .../loki/api/LokiPublicChatManager.kt | 20 ++++++------ .../loki/api/LokiPublicChatPoller.kt | 13 ++------ .../OptimizedMessageNotifier.java | 32 +++++++++---------- 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 32f464b1f6..dc5b73d2ad 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -226,9 +226,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc executePendingContactSync(); KeyCachingService.onAppForegrounded(this); // Loki - if (lokiPoller != null) { lokiPoller.shouldCatchUp(); } + if (lokiPoller != null) { lokiPoller.setCaughtUp(false); } startPollingIfNeeded(); - lokiPublicChatManager.shouldAllCatchUp(); + lokiPublicChatManager.markAllAsNotCaughtUp(); lokiPublicChatManager.startPollersIfNeeded(); } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 44e29fd1c8..3a5a0be5df 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -159,7 +159,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private long messageId; private long smsMessageId; - //Loki private MessageNotifier messageNotifier; @Inject SignalServiceMessageSender messageSender; diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt index 93f1bfc7a1..ae2e1fb2fc 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatManager.kt @@ -20,21 +20,21 @@ class LokiPublicChatManager(private val context: Context) { private val observers = mutableMapOf() private var isPolling = false - public fun isAllCatchUp():Boolean { - var isAllCatchUp = true + public fun areAllCaughtUp():Boolean { + var areAllCaughtUp = true refreshChatsAndPollers() - for ((threadId, chat) in chats) { - val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat) - isAllCatchUp = isAllCatchUp() && poller.isCatchUp() + for ((threadID, chat) in chats) { + val poller = pollers[threadID] ?: LokiPublicChatPoller(context, chat) + areAllCaughtUp = areAllCaughtUp && poller.isCaughtUp } - return isAllCatchUp + return areAllCaughtUp } - public fun shouldAllCatchUp() { + public fun markAllAsNotCaughtUp() { refreshChatsAndPollers() - for ((threadId, chat) in chats) { - val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat) - poller.shouldCatchUp() + for ((threadID, chat) in chats) { + val poller = pollers[threadID] ?: LokiPublicChatPoller(context, chat) + poller.isCaughtUp = false } } diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt index 0fed922b7c..17770c77a4 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPublicChatPoller.kt @@ -34,8 +34,7 @@ import java.util.* class LokiPublicChatPoller(private val context: Context, private val group: LokiPublicChat) { private val handler = Handler() private var hasStarted = false - - private var isCatchUp = false + public var isCaughtUp = false // region Convenience private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) @@ -84,14 +83,6 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki } // endregion - fun isCatchUp(): Boolean { - return isCatchUp - } - - fun shouldCatchUp() { - isCatchUp = false - } - // region Settings companion object { private val pollForNewMessagesInterval: Long = 4 * 1000 @@ -259,7 +250,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki processIncomingMessage(message) } } - isCatchUp = true + isCaughtUp = true }.fail { Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.") } diff --git a/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java index ed812d5a87..46e718359a 100644 --- a/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -2,18 +2,17 @@ package org.thoughtcrime.securesms.notifications; import android.content.Context; +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; + import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.Debouncer; -import org.thoughtcrime.securesms.util.Throttler; import org.whispersystems.signalservice.loki.api.LokiPoller; import java.util.concurrent.TimeUnit; -import androidx.annotation.MainThread; -import androidx.annotation.NonNull; - public class OptimizedMessageNotifier implements MessageNotifier { private final MessageNotifier wrapped; private final Debouncer debouncer; @@ -42,12 +41,12 @@ public class OptimizedMessageNotifier implements MessageNotifier { public void updateNotification(@NonNull Context context) { LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; - Boolean isCatchUp = false; + Boolean isCaughtUp = false; if (lokiPoller != null && lokiPublicChatManager != null) { - isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + isCaughtUp = lokiPoller.isCaughtUp() && lokiPublicChatManager.areAllCaughtUp(); } - if (isCatchUp) { + if (isCaughtUp) { wrapped.updateNotification(context); } else { debouncer.publish(() -> wrapped.updateNotification(context)); @@ -58,12 +57,12 @@ public class OptimizedMessageNotifier implements MessageNotifier { public void updateNotification(@NonNull Context context, long threadId) { LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; - Boolean isCatchUp = false; + Boolean isCaughtUp = false; if (lokiPoller != null && lokiPublicChatManager != null) { - isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + isCaughtUp = lokiPoller.isCaughtUp() && lokiPublicChatManager.areAllCaughtUp(); } - if (isCatchUp) { + if (isCaughtUp) { wrapped.updateNotification(context, threadId); } else { debouncer.publish(() -> wrapped.updateNotification(context, threadId)); @@ -74,12 +73,12 @@ public class OptimizedMessageNotifier implements MessageNotifier { public void updateNotification(@NonNull Context context, long threadId, boolean signal) { LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; - Boolean isCatchUp = false; + Boolean isCaughtUp = false; if (lokiPoller != null && lokiPublicChatManager != null) { - isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + isCaughtUp = lokiPoller.isCaughtUp() && lokiPublicChatManager.areAllCaughtUp(); } - if (isCatchUp) { + if (isCaughtUp) { wrapped.updateNotification(context, threadId, signal); } else { debouncer.publish(() -> wrapped.updateNotification(context, threadId, signal)); @@ -90,19 +89,18 @@ public class OptimizedMessageNotifier implements MessageNotifier { public void updateNotification(@android.support.annotation.NonNull Context context, boolean signal, int reminderCount) { LokiPoller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; - Boolean isCatchUp = false; + Boolean isCaughtUp = false; if (lokiPoller != null && lokiPublicChatManager != null) { - isCatchUp = lokiPoller.isCatchUp() && lokiPublicChatManager.isAllCatchUp(); + isCaughtUp = lokiPoller.isCaughtUp() && lokiPublicChatManager.areAllCaughtUp(); } - if (isCatchUp) { + if (isCaughtUp) { wrapped.updateNotification(context, signal, reminderCount); } else { debouncer.publish(() -> wrapped.updateNotification(context, signal, reminderCount)); } } - @Override public void clearReminder(@NonNull Context context) { wrapped.clearReminder(context); } }