NotificationThread migration.
This commit is contained in:
parent
af9465fefe
commit
eaf36be9f6
39 changed files with 432 additions and 254 deletions
|
@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.emoji.EmojiFiles
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob
|
||||||
import org.thoughtcrime.securesms.jobs.CreateReleaseChannelJob
|
import org.thoughtcrime.securesms.jobs.CreateReleaseChannelJob
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
|
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ class InternalSettingsRepository(context: Context) {
|
||||||
SignalDatabase.attachments.getAttachmentsForMessage(insertResult.messageId)
|
SignalDatabase.attachments.getAttachmentsForMessage(insertResult.messageId)
|
||||||
.forEach { ApplicationDependencies.getJobManager().add(AttachmentDownloadJob(insertResult.messageId, it.attachmentId, false)) }
|
.forEach { ApplicationDependencies.getJobManager().add(AttachmentDownloadJob(insertResult.messageId, it.attachmentId, false)) }
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.threadId)
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.threadId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName
|
import org.thoughtcrime.securesms.profiles.ProfileName
|
||||||
|
@ -214,7 +215,7 @@ object ContactDiscovery {
|
||||||
.forEach { result ->
|
.forEach { result ->
|
||||||
val hour = Calendar.getInstance()[Calendar.HOUR_OF_DAY]
|
val hour = Calendar.getInstance()[Calendar.HOUR_OF_DAY]
|
||||||
if (hour in 9..22) {
|
if (hour in 9..22) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, result.threadId, true)
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(result.threadId), true)
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Not notifying of a new user due to the time of day. (Hour: $hour)")
|
Log.i(TAG, "Not notifying of a new user due to the time of day. (Hour: $hour)")
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,6 +240,7 @@ import org.thoughtcrime.securesms.mms.SlideFactory.MediaType;
|
||||||
import org.thoughtcrime.securesms.mms.StickerSlide;
|
import org.thoughtcrime.securesms.mms.StickerSlide;
|
||||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.payments.CanNotSendPaymentDialog;
|
import org.thoughtcrime.securesms.payments.CanNotSendPaymentDialog;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.profiles.spoofing.ReviewBannerView;
|
import org.thoughtcrime.securesms.profiles.spoofing.ReviewBannerView;
|
||||||
|
@ -884,7 +885,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
private void setVisibleThread(long threadId) {
|
private void setVisibleThread(long threadId) {
|
||||||
if (!isInBubble()) {
|
if (!isInBubble()) {
|
||||||
// TODO [alex] LargeScreenSupport -- Inform MainActivityViewModel that the conversation was opened.
|
// TODO [alex] LargeScreenSupport -- Inform MainActivityViewModel that the conversation was opened.
|
||||||
ApplicationDependencies.getMessageNotifier().setVisibleThread(threadId);
|
ApplicationDependencies.getMessageNotifier().setVisibleThread(NotificationThread.forConversation(threadId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet;
|
||||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageId;
|
import org.thoughtcrime.securesms.database.model.MessageId;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.database.model.ParentStoryId;
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.StoryResult;
|
import org.thoughtcrime.securesms.database.model.StoryResult;
|
||||||
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||||
|
@ -200,6 +201,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||||
public abstract @Nullable Long getOldestStorySendTimestamp();
|
public abstract @Nullable Long getOldestStorySendTimestamp();
|
||||||
public abstract int deleteStoriesOlderThan(long timestamp);
|
public abstract int deleteStoriesOlderThan(long timestamp);
|
||||||
public abstract @NonNull MessageDatabase.Reader getUnreadStories(@NonNull RecipientId recipientId, int limit);
|
public abstract @NonNull MessageDatabase.Reader getUnreadStories(@NonNull RecipientId recipientId, int limit);
|
||||||
|
public abstract @Nullable ParentStoryId.GroupReply getParentStoryIdForGroupReply(long messageId);
|
||||||
|
|
||||||
public abstract @NonNull StoryViewState getStoryViewState(@NonNull RecipientId recipientId);
|
public abstract @NonNull StoryViewState getStoryViewState(@NonNull RecipientId recipientId);
|
||||||
public abstract void updateViewedStories(@NonNull Set<SyncMessageId> syncMessageIds);
|
public abstract void updateViewedStories(@NonNull Set<SyncMessageId> syncMessageIds);
|
||||||
|
|
|
@ -614,6 +614,25 @@ public class MmsDatabase extends MessageDatabase {
|
||||||
return new Reader(rawQuery(query, args, false, limit));
|
return new Reader(rawQuery(query, args, false, limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ParentStoryId.GroupReply getParentStoryIdForGroupReply(long messageId) {
|
||||||
|
String[] projection = SqlUtil.buildArgs(PARENT_STORY_ID);
|
||||||
|
String[] args = SqlUtil.buildArgs(messageId);
|
||||||
|
|
||||||
|
try (Cursor cursor = getReadableDatabase().query(TABLE_NAME, projection, ID_WHERE, args, null, null, null)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
ParentStoryId parentStoryId = ParentStoryId.deserialize(CursorUtil.requireLong(cursor, PARENT_STORY_ID));
|
||||||
|
if (parentStoryId != null && parentStoryId.isGroupReply()) {
|
||||||
|
return (ParentStoryId.GroupReply) parentStoryId;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull StoryViewState getStoryViewState(@NonNull RecipientId recipientId) {
|
public @NonNull StoryViewState getStoryViewState(@NonNull RecipientId recipientId) {
|
||||||
if (!Stories.isFeatureEnabled()) {
|
if (!Stories.isFeatureEnabled()) {
|
||||||
|
|
|
@ -259,20 +259,29 @@ public class MmsSmsDatabase extends Database {
|
||||||
}
|
}
|
||||||
stickyQuery.append("(")
|
stickyQuery.append("(")
|
||||||
.append(MmsSmsColumns.THREAD_ID + " = ")
|
.append(MmsSmsColumns.THREAD_ID + " = ")
|
||||||
.append(stickyThread.getThreadId())
|
.append(stickyThread.getNotificationThread().getThreadId())
|
||||||
.append(" AND ")
|
.append(" AND ")
|
||||||
.append(MmsSmsColumns.NORMALIZED_DATE_RECEIVED)
|
.append(MmsSmsColumns.NORMALIZED_DATE_RECEIVED)
|
||||||
.append(" >= ")
|
.append(" >= ")
|
||||||
.append(stickyThread.getEarliestTimestamp())
|
.append(stickyThread.getEarliestTimestamp())
|
||||||
|
.append(getStickyWherePartForParentStoryId(stickyThread.getNotificationThread().getGroupStoryId()))
|
||||||
.append(")");
|
.append(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
|
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
|
||||||
String selection = MmsSmsColumns.NOTIFIED + " = 0 AND " + MmsDatabase.STORY_TYPE + " = 0 AND " + MmsDatabase.PARENT_STORY_ID + " <= 0 AND (" + MmsSmsColumns.READ + " = 0 OR " + MmsSmsColumns.REACTIONS_UNREAD + " = 1" + (stickyQuery.length() > 0 ? " OR (" + stickyQuery.toString() + ")" : "") + ")";
|
String selection = MmsSmsColumns.NOTIFIED + " = 0 AND " + MmsDatabase.STORY_TYPE + " = 0 AND (" + MmsSmsColumns.READ + " = 0 OR " + MmsSmsColumns.REACTIONS_UNREAD + " = 1" + (stickyQuery.length() > 0 ? " OR (" + stickyQuery + ")" : "") + ")";
|
||||||
|
|
||||||
return queryTables(PROJECTION, selection, order, null, true);
|
return queryTables(PROJECTION, selection, order, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @NonNull String getStickyWherePartForParentStoryId(@Nullable Long parentStoryId) {
|
||||||
|
if (parentStoryId == null) {
|
||||||
|
return " AND " + MmsDatabase.PARENT_STORY_ID + " <= 0";
|
||||||
|
}
|
||||||
|
|
||||||
|
return " AND " + MmsDatabase.PARENT_STORY_ID + " = " + parentStoryId;
|
||||||
|
}
|
||||||
|
|
||||||
public int getUnreadCount(long threadId) {
|
public int getUnreadCount(long threadId) {
|
||||||
String selection = MmsSmsColumns.READ + " = 0 AND " + MmsDatabase.STORY_TYPE + " = 0 AND " + MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsDatabase.PARENT_STORY_ID + " <= 0";
|
String selection = MmsSmsColumns.READ + " = 0 AND " + MmsDatabase.STORY_TYPE + " = 0 AND " + MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsDatabase.PARENT_STORY_ID + " <= 0";
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||||
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil;
|
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageId;
|
import org.thoughtcrime.securesms.database.model.MessageId;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.database.model.ParentStoryId;
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.StoryResult;
|
import org.thoughtcrime.securesms.database.model.StoryResult;
|
||||||
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
import org.thoughtcrime.securesms.database.model.StoryViewState;
|
||||||
|
@ -1471,6 +1472,11 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ParentStoryId.GroupReply getParentStoryIdForGroupReply(long messageId) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageRecord getMessageRecord(long messageId) throws NoSuchMessageException {
|
public MessageRecord getMessageRecord(long messageId) throws NoSuchMessageException {
|
||||||
return getSmsMessage(messageId);
|
return getSmsMessage(messageId);
|
||||||
|
|
|
@ -197,8 +197,9 @@ object SignalDatabaseMigrations {
|
||||||
private const val GROUP_SERVICE_ID = 141
|
private const val GROUP_SERVICE_ID = 141
|
||||||
private const val QUOTE_TYPE = 142
|
private const val QUOTE_TYPE = 142
|
||||||
private const val STORY_SYNCS = 143
|
private const val STORY_SYNCS = 143
|
||||||
|
private const val GROUP_STORY_NOTIFICATIONS = 144
|
||||||
|
|
||||||
const val DATABASE_VERSION = 143
|
const val DATABASE_VERSION = 144
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
@ -2567,6 +2568,10 @@ object SignalDatabaseMigrations {
|
||||||
db.execSQL("ALTER TABLE story_sends_tmp RENAME TO story_sends")
|
db.execSQL("ALTER TABLE story_sends_tmp RENAME TO story_sends")
|
||||||
db.execSQL("CREATE INDEX story_sends_recipient_id_sent_timestamp_allows_replies_index ON story_sends (recipient_id, sent_timestamp, allows_replies)")
|
db.execSQL("CREATE INDEX story_sends_recipient_id_sent_timestamp_allows_replies_index ON story_sends (recipient_id, sent_timestamp, allows_replies)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < GROUP_STORY_NOTIFICATIONS) {
|
||||||
|
db.execSQL("UPDATE mms SET read = 1 WHERE parent_story_id > 0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.jobs.AvatarGroupsV1DownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage;
|
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage;
|
||||||
|
@ -254,7 +255,7 @@ public final class GroupV1MessageProcessor {
|
||||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
return insertResult.get().getThreadId();
|
return insertResult.get().getThreadId();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobLogger;
|
import org.thoughtcrime.securesms.jobmanager.JobLogger;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel;
|
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||||
|
@ -119,7 +120,7 @@ public final class AttachmentDownloadJob extends BaseJob {
|
||||||
doWork();
|
doWork();
|
||||||
|
|
||||||
if (!SignalDatabase.mms().isStory(messageId)) {
|
if (!SignalDatabase.mms().isStory(messageId)) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, 0);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
@ -121,7 +122,7 @@ public class AutomaticSessionResetJob extends BaseJob {
|
||||||
|
|
||||||
private void insertLocalMessage() {
|
private void insertLocalMessage() {
|
||||||
MessageDatabase.InsertResult result = SignalDatabase.sms().insertChatSessionRefreshedMessage(recipientId, deviceId, sentTimestamp);
|
MessageDatabase.InsertResult result = SignalDatabase.sms().insertChatSessionRefreshedMessage(recipientId, deviceId, sentTimestamp);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, result.getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(result.getThreadId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendNullMessage() throws IOException {
|
private void sendNullMessage() throws IOException {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
||||||
import org.thoughtcrime.securesms.mms.PartParser;
|
import org.thoughtcrime.securesms.mms.PartParser;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
@ -174,7 +175,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||||
|
|
||||||
if (automatic) {
|
if (automatic) {
|
||||||
database.markIncomingNotificationReceived(threadId);
|
database.markIncomingNotificationReceived(threadId);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(threadId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +255,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
database.deleteMessage(messageId);
|
database.deleteMessage(messageId);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +267,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||||
|
|
||||||
if (automatic) {
|
if (automatic) {
|
||||||
db.markIncomingNotificationReceived(threadId);
|
db.markIncomingNotificationReceived(threadId);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(threadId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.google.android.mms.pdu_alt.SendReq;
|
||||||
import com.google.android.mms.smil.SmilHelper;
|
import com.google.android.mms.smil.SmilHelper;
|
||||||
import com.klinker.android.send_message.Utils;
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
|
import org.signal.core.util.Hex;
|
||||||
import org.signal.core.util.StreamUtil;
|
import org.signal.core.util.StreamUtil;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
|
@ -44,11 +45,11 @@ import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.MmsSendResult;
|
import org.thoughtcrime.securesms.mms.MmsSendResult;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
import org.signal.core.util.Hex;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -347,7 +348,7 @@ public final class MmsSendJob extends SendJob {
|
||||||
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
||||||
|
|
||||||
if (recipient != null) {
|
if (recipient != null) {
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, threadId);
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, NotificationThread.forConversation(threadId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.Mention;
|
import org.thoughtcrime.securesms.database.model.Mention;
|
||||||
|
import org.thoughtcrime.securesms.database.model.ParentStoryId;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
@ -43,6 +44,7 @@ import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.mms.QuoteModel;
|
import org.thoughtcrime.securesms.mms.QuoteModel;
|
||||||
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
|
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
@ -291,9 +293,10 @@ public abstract class PushSendJob extends SendJob {
|
||||||
protected static void notifyMediaMessageDeliveryFailed(Context context, long messageId) {
|
protected static void notifyMediaMessageDeliveryFailed(Context context, long messageId) {
|
||||||
long threadId = SignalDatabase.mms().getThreadIdForMessage(messageId);
|
long threadId = SignalDatabase.mms().getThreadIdForMessage(messageId);
|
||||||
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
||||||
|
ParentStoryId.GroupReply groupReplyStoryId = SignalDatabase.mms().getParentStoryIdForGroupReply(messageId);
|
||||||
|
|
||||||
if (threadId != -1 && recipient != null) {
|
if (threadId != -1 && recipient != null) {
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, threadId);
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, NotificationThread.fromThreadAndReply(threadId, groupReplyStoryId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +514,8 @@ public abstract class PushSendJob extends SendJob {
|
||||||
SignalStore.rateLimit().markNeedsRecaptcha(proofRequired.getToken());
|
SignalStore.rateLimit().markNeedsRecaptcha(proofRequired.getToken());
|
||||||
|
|
||||||
if (recipient != null) {
|
if (recipient != null) {
|
||||||
ApplicationDependencies.getMessageNotifier().notifyProofRequired(context, recipient, threadId);
|
ParentStoryId.GroupReply groupReply = SignalDatabase.mms().getParentStoryIdForGroupReply(messageId);
|
||||||
|
ApplicationDependencies.getMessageNotifier().notifyProofRequired(context, recipient, NotificationThread.fromThreadAndReply(threadId, groupReply));
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "[Proof Required] No recipient! Couldn't notify.");
|
Log.w(TAG, "[Proof Required] No recipient! Couldn't notify.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
@ -126,7 +127,7 @@ public class PushTextSendJob extends PushSendJob {
|
||||||
} catch (InsecureFallbackApprovalException e) {
|
} catch (InsecureFallbackApprovalException e) {
|
||||||
warn(TAG, String.valueOf(record.getDateSent()), "Failure", e);
|
warn(TAG, String.valueOf(record.getDateSent()), "Failure", e);
|
||||||
database.markAsPendingInsecureSmsFallback(record.getId());
|
database.markAsPendingInsecureSmsFallback(record.getId());
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), NotificationThread.forConversation(record.getThreadId()));
|
||||||
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
|
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
|
||||||
} catch (UntrustedIdentityException e) {
|
} catch (UntrustedIdentityException e) {
|
||||||
warn(TAG, String.valueOf(record.getDateSent()), "Failure", e);
|
warn(TAG, String.valueOf(record.getDateSent()), "Failure", e);
|
||||||
|
@ -156,7 +157,7 @@ public class PushTextSendJob extends PushSendJob {
|
||||||
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
||||||
|
|
||||||
if (threadId != -1 && recipient != null) {
|
if (threadId != -1 && recipient != null) {
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, threadId);
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, NotificationThread.forConversation(threadId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.jobmanager.Data
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
|
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
|
||||||
import org.thoughtcrime.securesms.s3.S3
|
import org.thoughtcrime.securesms.s3.S3
|
||||||
|
@ -187,7 +188,7 @@ class RetrieveReleaseChannelJob private constructor(private val force: Boolean,
|
||||||
SignalDatabase.attachments.getAttachmentsForMessage(insertResult.messageId)
|
SignalDatabase.attachments.getAttachmentsForMessage(insertResult.messageId)
|
||||||
.forEach { ApplicationDependencies.getJobManager().add(AttachmentDownloadJob(insertResult.messageId, it.attachmentId, false)) }
|
.forEach { ApplicationDependencies.getJobManager().add(AttachmentDownloadJob(insertResult.messageId, it.attachmentId, false)) }
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.threadId)
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.threadId))
|
||||||
TrimThreadJob.enqueueAsync(insertResult.threadId)
|
TrimThreadJob.enqueueAsync(insertResult.threadId)
|
||||||
|
|
||||||
highestVersion = max(highestVersion, note.releaseNote.androidMinVersion!!.toInt())
|
highestVersion = max(highestVersion, note.releaseNote.androidMinVersion!!.toInt())
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.service.VerificationCodeParser;
|
import org.thoughtcrime.securesms.service.VerificationCodeParser;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||||
|
@ -123,7 +124,7 @@ public class SmsReceiveJob extends BaseJob {
|
||||||
Optional<InsertResult> insertResult = storeMessage(message.get());
|
Optional<InsertResult> insertResult = storeMessage(message.get());
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
} else if (message.isPresent()) {
|
} else if (message.isPresent()) {
|
||||||
Log.w(TAG, "Received an SMS from a blocked user. Ignoring.");
|
Log.w(TAG, "Received an SMS from a blocked user. Ignoring.");
|
||||||
|
|
|
@ -20,11 +20,11 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
|
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public class SmsSendJob extends SendJob {
|
||||||
} catch (UndeliverableMessageException ude) {
|
} catch (UndeliverableMessageException ude) {
|
||||||
warn(TAG, ude);
|
warn(TAG, ude);
|
||||||
SignalDatabase.sms().markAsSentFailed(record.getId());
|
SignalDatabase.sms().markAsSentFailed(record.getId());
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), NotificationThread.fromMessageRecord(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ public class SmsSendJob extends SendJob {
|
||||||
SignalDatabase.sms().markAsSentFailed(messageId);
|
SignalDatabase.sms().markAsSentFailed(messageId);
|
||||||
|
|
||||||
if (threadId != -1 && recipient != null) {
|
if (threadId != -1 && recipient != null) {
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, threadId);
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, NotificationThread.forConversation(threadId));
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Could not find message! threadId: " + threadId + ", recipient: " + (recipient != null ? recipient.getId().toString() : "null"));
|
Log.w(TAG, "Could not find message! threadId: " + threadId + ", recipient: " + (recipient != null ? recipient.getId().toString() : "null"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
|
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
|
||||||
|
|
||||||
public class SmsSentJob extends BaseJob {
|
public class SmsSentJob extends BaseJob {
|
||||||
|
@ -108,7 +109,7 @@ public class SmsSentJob extends BaseJob {
|
||||||
if (isMultipart) {
|
if (isMultipart) {
|
||||||
Log.w(TAG, "Service connectivity problem, but not retrying due to multipart");
|
Log.w(TAG, "Service connectivity problem, but not retrying due to multipart");
|
||||||
database.markAsSentFailed(messageId);
|
database.markAsSentFailed(messageId);
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), NotificationThread.forConversation(record.getThreadId()));
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Service connectivity problem, requeuing...");
|
Log.w(TAG, "Service connectivity problem, requeuing...");
|
||||||
ApplicationDependencies.getJobManager().add(new SmsSendJob(messageId, record.getIndividualRecipient(), runAttempt + 1));
|
ApplicationDependencies.getJobManager().add(new SmsSendJob(messageId, record.getIndividualRecipient(), runAttempt + 1));
|
||||||
|
@ -116,7 +117,7 @@ public class SmsSentJob extends BaseJob {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
database.markAsSentFailed(messageId);
|
database.markAsSentFailed(messageId);
|
||||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), NotificationThread.forConversation(record.getThreadId()));
|
||||||
}
|
}
|
||||||
} catch (NoSuchMessageException e) {
|
} catch (NoSuchMessageException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
|
|
|
@ -113,6 +113,7 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.mms.StickerSlide;
|
import org.thoughtcrime.securesms.mms.StickerSlide;
|
||||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.payments.MobileCoinPublicAddress;
|
import org.thoughtcrime.securesms.payments.MobileCoinPublicAddress;
|
||||||
import org.thoughtcrime.securesms.ratelimit.RateLimitUtil;
|
import org.thoughtcrime.securesms.ratelimit.RateLimitUtil;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -411,7 +412,7 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (threadId != null) {
|
if (threadId != null) {
|
||||||
ThreadDatabase.ConversationMetadata metadata = SignalDatabase.threads().getConversationMetadata(threadId);
|
ThreadDatabase.ConversationMetadata metadata = SignalDatabase.threads().getConversationMetadata(threadId);
|
||||||
long visibleThread = ApplicationDependencies.getMessageNotifier().getVisibleThread();
|
long visibleThread = ApplicationDependencies.getMessageNotifier().getVisibleThread().map(NotificationThread::getThreadId).orElse(-1L);
|
||||||
|
|
||||||
if (threadId != visibleThread && metadata.getLastSeen() > 0 && metadata.getLastSeen() < pending.getReceivedTimestamp()) {
|
if (threadId != visibleThread && metadata.getLastSeen() > 0 && metadata.getLastSeen() < pending.getReceivedTimestamp()) {
|
||||||
receivedTime = pending.getReceivedTimestamp();
|
receivedTime = pending.getReceivedTimestamp();
|
||||||
|
@ -754,7 +755,7 @@ public final class MessageContentProcessor {
|
||||||
ApplicationDependencies.getProtocolStore().aci().deleteAllSessions(content.getSender().getIdentifier());
|
ApplicationDependencies.getProtocolStore().aci().deleteAllSessions(content.getSender().getIdentifier());
|
||||||
|
|
||||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
|
|
||||||
return new MessageId(insertResult.get().getMessageId(), true);
|
return new MessageId(insertResult.get().getMessageId(), true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -982,7 +983,7 @@ public final class MessageContentProcessor {
|
||||||
} else {
|
} else {
|
||||||
ReactionRecord reactionRecord = new ReactionRecord(reaction.getEmoji(), senderRecipient.getId(), message.getTimestamp(), System.currentTimeMillis());
|
ReactionRecord reactionRecord = new ReactionRecord(reaction.getEmoji(), senderRecipient.getId(), message.getTimestamp(), System.currentTimeMillis());
|
||||||
SignalDatabase.reactions().addReaction(targetMessageId, reactionRecord);
|
SignalDatabase.reactions().addReaction(targetMessageId, reactionRecord);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.fromMessageRecord(targetMessage), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MessageId(targetMessage.getId(), targetMessage.isMms());
|
return new MessageId(targetMessage.getId(), targetMessage.isMms());
|
||||||
|
@ -998,7 +999,7 @@ public final class MessageContentProcessor {
|
||||||
if (targetMessage != null && RemoteDeleteUtil.isValidReceive(targetMessage, senderRecipient, content.getServerReceivedTimestamp())) {
|
if (targetMessage != null && RemoteDeleteUtil.isValidReceive(targetMessage, senderRecipient, content.getServerReceivedTimestamp())) {
|
||||||
MessageDatabase db = targetMessage.isMms() ? SignalDatabase.mms() : SignalDatabase.sms();
|
MessageDatabase db = targetMessage.isMms() ? SignalDatabase.mms() : SignalDatabase.sms();
|
||||||
db.markAsRemoteDelete(targetMessage.getId());
|
db.markAsRemoteDelete(targetMessage.getId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.fromMessageRecord(targetMessage), false);
|
||||||
return new MessageId(targetMessage.getId(), targetMessage.isMms());
|
return new MessageId(targetMessage.getId(), targetMessage.isMms());
|
||||||
} else if (targetMessage == null) {
|
} else if (targetMessage == null) {
|
||||||
warn(String.valueOf(content.getTimestamp()), "[handleRemoteDelete] Could not find matching message! timestamp: " + delete.getTargetSentTimestamp() + " author: " + senderRecipient.getId());
|
warn(String.valueOf(content.getTimestamp()), "[handleRemoteDelete] Could not find matching message! timestamp: " + delete.getTargetSentTimestamp() + " author: " + senderRecipient.getId());
|
||||||
|
@ -1776,7 +1777,7 @@ public final class MessageContentProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
TrimThreadJob.enqueueAsync(insertResult.get().getThreadId());
|
TrimThreadJob.enqueueAsync(insertResult.get().getThreadId());
|
||||||
|
|
||||||
return new MessageId(insertResult.get().getMessageId(), true);
|
return new MessageId(insertResult.get().getMessageId(), true);
|
||||||
|
@ -1860,7 +1861,7 @@ public final class MessageContentProcessor {
|
||||||
ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
|
ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
TrimThreadJob.enqueueAsync(insertResult.get().getThreadId());
|
TrimThreadJob.enqueueAsync(insertResult.get().getThreadId());
|
||||||
|
|
||||||
if (message.isViewOnce()) {
|
if (message.isViewOnce()) {
|
||||||
|
@ -2324,7 +2325,7 @@ public final class MessageContentProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
return new MessageId(insertResult.get().getMessageId(), false);
|
return new MessageId(insertResult.get().getMessageId(), false);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -2411,7 +2412,7 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
|
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
|
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
|
||||||
|
@ -2430,7 +2431,7 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
|
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
smsDatabase.markAsDecryptFailed(smsMessageId.get());
|
smsDatabase.markAsDecryptFailed(smsMessageId.get());
|
||||||
|
@ -2452,7 +2453,7 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
smsDatabase.markAsUnsupportedProtocolVersion(insertResult.get().getMessageId());
|
smsDatabase.markAsUnsupportedProtocolVersion(insertResult.get().getMessageId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
smsDatabase.markAsNoSession(smsMessageId.get());
|
smsDatabase.markAsNoSession(smsMessageId.get());
|
||||||
|
@ -2474,7 +2475,7 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
smsDatabase.markAsInvalidMessage(insertResult.get().getMessageId());
|
smsDatabase.markAsInvalidMessage(insertResult.get().getMessageId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
smsDatabase.markAsNoSession(smsMessageId.get());
|
smsDatabase.markAsNoSession(smsMessageId.get());
|
||||||
|
@ -2493,7 +2494,7 @@ public final class MessageContentProcessor {
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
|
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
smsDatabase.markAsLegacyVersion(smsMessageId.get());
|
smsDatabase.markAsLegacyVersion(smsMessageId.get());
|
||||||
|
|
|
@ -8,6 +8,9 @@ import android.content.Intent;
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class DeleteNotificationReceiver extends BroadcastReceiver {
|
public class DeleteNotificationReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@ -15,7 +18,7 @@ public class DeleteNotificationReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
public static final String EXTRA_IDS = "message_ids";
|
public static final String EXTRA_IDS = "message_ids";
|
||||||
public static final String EXTRA_MMS = "is_mms";
|
public static final String EXTRA_MMS = "is_mms";
|
||||||
public static final String EXTRA_THREAD_IDS = "thread_ids";
|
public static final String EXTRA_THREADS = "threads";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(final Context context, Intent intent) {
|
public void onReceive(final Context context, Intent intent) {
|
||||||
|
@ -25,11 +28,11 @@ public class DeleteNotificationReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
final long[] ids = intent.getLongArrayExtra(EXTRA_IDS);
|
final long[] ids = intent.getLongArrayExtra(EXTRA_IDS);
|
||||||
final boolean[] mms = intent.getBooleanArrayExtra(EXTRA_MMS);
|
final boolean[] mms = intent.getBooleanArrayExtra(EXTRA_MMS);
|
||||||
final long[] threadIds = intent.getLongArrayExtra(EXTRA_THREAD_IDS);
|
final ArrayList<NotificationThread> threads = intent.getParcelableArrayListExtra(EXTRA_THREADS);
|
||||||
|
|
||||||
if (threadIds != null) {
|
if (threads != null) {
|
||||||
for (long threadId : threadIds) {
|
for (NotificationThread thread : threads) {
|
||||||
notifier.removeStickyThread(threadId);
|
notifier.removeStickyThread(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,11 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -30,7 +32,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private static final String TAG = Log.tag(MarkReadReceiver.class);
|
private static final String TAG = Log.tag(MarkReadReceiver.class);
|
||||||
public static final String CLEAR_ACTION = "org.thoughtcrime.securesms.notifications.CLEAR";
|
public static final String CLEAR_ACTION = "org.thoughtcrime.securesms.notifications.CLEAR";
|
||||||
public static final String THREAD_IDS_EXTRA = "thread_ids";
|
public static final String THREADS_EXTRA = "threads";
|
||||||
public static final String NOTIFICATION_ID_EXTRA = "notification_id";
|
public static final String NOTIFICATION_ID_EXTRA = "notification_id";
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@ -39,12 +41,12 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
||||||
if (!CLEAR_ACTION.equals(intent.getAction()))
|
if (!CLEAR_ACTION.equals(intent.getAction()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA);
|
final ArrayList<NotificationThread> threads = intent.getParcelableArrayListExtra(THREADS_EXTRA);
|
||||||
|
|
||||||
if (threadIds != null) {
|
if (threads != null) {
|
||||||
MessageNotifier notifier = ApplicationDependencies.getMessageNotifier();
|
MessageNotifier notifier = ApplicationDependencies.getMessageNotifier();
|
||||||
for (long threadId : threadIds) {
|
for (NotificationThread thread : threads) {
|
||||||
notifier.removeStickyThread(threadId);
|
notifier.removeStickyThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCancellationHelper.cancelLegacy(context, intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1));
|
NotificationCancellationHelper.cancelLegacy(context, intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1));
|
||||||
|
@ -53,9 +55,9 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
List<MarkedMessageInfo> messageIdsCollection = new LinkedList<>();
|
List<MarkedMessageInfo> messageIdsCollection = new LinkedList<>();
|
||||||
|
|
||||||
for (long threadId : threadIds) {
|
for (NotificationThread thread : threads) {
|
||||||
Log.i(TAG, "Marking as read: " + threadId);
|
Log.i(TAG, "Marking as read: " + thread);
|
||||||
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setRead(threadId, true);
|
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setRead(thread.getThreadId(), true);
|
||||||
messageIdsCollection.addAll(messageIds);
|
messageIdsCollection.addAll(messageIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,28 +5,32 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.BubbleUtil;
|
import org.thoughtcrime.securesms.util.BubbleUtil;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface MessageNotifier {
|
public interface MessageNotifier {
|
||||||
void setVisibleThread(long threadId);
|
void setVisibleThread(@Nullable NotificationThread notificationThread);
|
||||||
long getVisibleThread();
|
@NonNull Optional<NotificationThread> getVisibleThread();
|
||||||
void clearVisibleThread();
|
void clearVisibleThread();
|
||||||
void setLastDesktopActivityTimestamp(long timestamp);
|
void setLastDesktopActivityTimestamp(long timestamp);
|
||||||
void notifyMessageDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, long threadId);
|
void notifyMessageDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, @NonNull NotificationThread notificationThread);
|
||||||
void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, long threadId);
|
void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, @NonNull NotificationThread notificationThread);
|
||||||
void cancelDelayedNotifications();
|
void cancelDelayedNotifications();
|
||||||
void updateNotification(@NonNull Context context);
|
void updateNotification(@NonNull Context context);
|
||||||
void updateNotification(@NonNull Context context, long threadId);
|
void updateNotification(@NonNull Context context, @NonNull NotificationThread notificationThread);
|
||||||
void updateNotification(@NonNull Context context, long threadId, @NonNull BubbleUtil.BubbleState defaultBubbleState);
|
void updateNotification(@NonNull Context context, @NonNull NotificationThread notificationThread, @NonNull BubbleUtil.BubbleState defaultBubbleState);
|
||||||
void updateNotification(@NonNull Context context, long threadId, boolean signal);
|
void updateNotification(@NonNull Context context, @NonNull NotificationThread notificationThread, boolean signal);
|
||||||
void updateNotification(@NonNull Context context, long threadId, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState);
|
void updateNotification(@NonNull Context context, @Nullable NotificationThread notificationThread, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState);
|
||||||
void clearReminder(@NonNull Context context);
|
void clearReminder(@NonNull Context context);
|
||||||
void addStickyThread(long threadId, long earliestTimestamp);
|
void addStickyThread(@NonNull NotificationThread notificationThread, long earliestTimestamp);
|
||||||
void removeStickyThread(long threadId);
|
void removeStickyThread(@NonNull NotificationThread notificationThread);
|
||||||
|
|
||||||
|
|
||||||
class ReminderReceiver extends BroadcastReceiver {
|
class ReminderReceiver extends BroadcastReceiver {
|
||||||
|
@ -35,7 +39,7 @@ public interface MessageNotifier {
|
||||||
public void onReceive(final Context context, final Intent intent) {
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
int reminderCount = intent.getIntExtra("reminder_count", 0);
|
int reminderCount = intent.getIntExtra("reminder_count", 0);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, -1, true, reminderCount + 1, BubbleUtil.BubbleState.HIDDEN);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, null, true, reminderCount + 1, BubbleUtil.BubbleState.HIDDEN);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2;
|
import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.BubbleUtil;
|
import org.thoughtcrime.securesms.util.BubbleUtil;
|
||||||
import org.thoughtcrime.securesms.util.ConversationUtil;
|
import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||||
|
@ -22,6 +23,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,9 +186,11 @@ public final class NotificationCancellationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
Long threadId = SignalDatabase.threads().getThreadIdFor(recipientId);
|
Long threadId = SignalDatabase.threads().getThreadIdFor(recipientId);
|
||||||
long focusedThreadId = ApplicationDependencies.getMessageNotifier().getVisibleThread();
|
Optional<NotificationThread> focusedThread = ApplicationDependencies.getMessageNotifier().getVisibleThread();
|
||||||
|
Long focusedThreadId = focusedThread.map(NotificationThread::getThreadId).orElse(null);
|
||||||
|
Long focusedGroupStoryId = focusedThread.map(NotificationThread::getGroupStoryId).orElse(null);
|
||||||
|
|
||||||
if (Objects.equals(threadId, focusedThreadId)) {
|
if (Objects.equals(threadId, focusedThreadId) && focusedGroupStoryId == null) {
|
||||||
Log.d(TAG, "isCancellable: user entered full screen thread.");
|
Log.d(TAG, "isCancellable: user entered full screen thread.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package org.thoughtcrime.securesms.notifications;
|
package org.thoughtcrime.securesms.notifications;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
|
|
||||||
public final class NotificationIds {
|
public final class NotificationIds {
|
||||||
|
|
||||||
public static final int FCM_FAILURE = 12;
|
public static final int FCM_FAILURE = 12;
|
||||||
|
@ -15,10 +19,25 @@ public final class NotificationIds {
|
||||||
public static final int DEVICE_TRANSFER = 625420;
|
public static final int DEVICE_TRANSFER = 625420;
|
||||||
public static final int DONOR_BADGE_FAILURE = 630001;
|
public static final int DONOR_BADGE_FAILURE = 630001;
|
||||||
public static final int FCM_FETCH = 630002;
|
public static final int FCM_FETCH = 630002;
|
||||||
|
public static final int STORY_THREAD = 700000;
|
||||||
|
public static final int MESSAGE_DELIVERY_FAILURE = 800000;
|
||||||
|
public static final int STORY_MESSAGE_DELIVERY_FAILURE = 900000;
|
||||||
|
|
||||||
private NotificationIds() { }
|
private NotificationIds() { }
|
||||||
|
|
||||||
public static int getNotificationIdForThread(long threadId) {
|
public static int getNotificationIdForThread(@NonNull NotificationThread notificationThread) {
|
||||||
return THREAD + (int) threadId;
|
if (notificationThread.getGroupStoryId() != null) {
|
||||||
|
return STORY_THREAD + notificationThread.getGroupStoryId().intValue();
|
||||||
|
} else {
|
||||||
|
return THREAD + (int) notificationThread.getThreadId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getNotificationIdForMessageDeliveryFailed(@NonNull NotificationThread notificationThread) {
|
||||||
|
if (notificationThread.getGroupStoryId() != null) {
|
||||||
|
return STORY_MESSAGE_DELIVERY_FAILURE + notificationThread.getGroupStoryId().intValue();
|
||||||
|
} else {
|
||||||
|
return MESSAGE_DELIVERY_FAILURE + (int) notificationThread.getThreadId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,18 @@ import android.os.Handler;
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
import androidx.annotation.MainThread;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.signal.core.util.ExceptionUtil;
|
import org.signal.core.util.ExceptionUtil;
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2;
|
import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.BubbleUtil;
|
import org.thoughtcrime.securesms.util.BubbleUtil;
|
||||||
import org.thoughtcrime.securesms.util.LeakyBucketLimiter;
|
import org.thoughtcrime.securesms.util.LeakyBucketLimiter;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses a leaky-bucket strategy to limiting notification updates.
|
* Uses a leaky-bucket strategy to limiting notification updates.
|
||||||
*/
|
*/
|
||||||
|
@ -29,12 +33,12 @@ public class OptimizedMessageNotifier implements MessageNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVisibleThread(long threadId) {
|
public void setVisibleThread(@Nullable NotificationThread notificationThread) {
|
||||||
getNotifier().setVisibleThread(threadId);
|
getNotifier().setVisibleThread(notificationThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getVisibleThread() {
|
public @NonNull Optional<NotificationThread> getVisibleThread() {
|
||||||
return getNotifier().getVisibleThread();
|
return getNotifier().getVisibleThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,13 +53,13 @@ public class OptimizedMessageNotifier implements MessageNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyMessageDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, long threadId) {
|
public void notifyMessageDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, @NonNull NotificationThread notificationThread) {
|
||||||
getNotifier().notifyMessageDeliveryFailed(context, recipient, threadId);
|
getNotifier().notifyMessageDeliveryFailed(context, recipient, notificationThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, long threadId) {
|
public void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, @NonNull NotificationThread notificationThread) {
|
||||||
getNotifier().notifyProofRequired(context, recipient, threadId);
|
getNotifier().notifyProofRequired(context, recipient, notificationThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,23 +73,23 @@ public class OptimizedMessageNotifier implements MessageNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateNotification(@NonNull Context context, long threadId) {
|
public void updateNotification(@NonNull Context context, @NonNull NotificationThread notificationThread) {
|
||||||
runOnLimiter(() -> getNotifier().updateNotification(context, threadId));
|
runOnLimiter(() -> getNotifier().updateNotification(context, notificationThread));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateNotification(@NonNull Context context, long threadId, @NonNull BubbleUtil.BubbleState defaultBubbleState) {
|
public void updateNotification(@NonNull Context context, @NonNull NotificationThread notificationThread, @NonNull BubbleUtil.BubbleState defaultBubbleState) {
|
||||||
runOnLimiter(() -> getNotifier().updateNotification(context, threadId, defaultBubbleState));
|
runOnLimiter(() -> getNotifier().updateNotification(context, notificationThread, defaultBubbleState));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateNotification(@NonNull Context context, long threadId, boolean signal) {
|
public void updateNotification(@NonNull Context context, @NonNull NotificationThread notificationThread, boolean signal) {
|
||||||
runOnLimiter(() -> getNotifier().updateNotification(context, threadId, signal));
|
runOnLimiter(() -> getNotifier().updateNotification(context, notificationThread, signal));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateNotification(@NonNull Context context, long threadId, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState) {
|
public void updateNotification(@NonNull Context context, @Nullable NotificationThread notificationThread, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState) {
|
||||||
runOnLimiter(() -> getNotifier().updateNotification(context, threadId, signal, reminderCount, defaultBubbleState));
|
runOnLimiter(() -> getNotifier().updateNotification(context, notificationThread, signal, reminderCount, defaultBubbleState));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,13 +98,13 @@ public class OptimizedMessageNotifier implements MessageNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addStickyThread(long threadId, long earliestTimestamp) {
|
public void addStickyThread(@NonNull NotificationThread notificationThread, long earliestTimestamp) {
|
||||||
getNotifier().addStickyThread(threadId, earliestTimestamp);
|
getNotifier().addStickyThread(notificationThread, earliestTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeStickyThread(long threadId) {
|
public void removeStickyThread(@NonNull NotificationThread notificationThread) {
|
||||||
getNotifier().removeStickyThread(threadId);
|
getNotifier().removeStickyThread(notificationThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runOnLimiter(@NonNull Runnable runnable) {
|
private void runOnLimiter(@NonNull Runnable runnable) {
|
||||||
|
|
|
@ -28,10 +28,12 @@ import androidx.core.app.RemoteInput;
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
|
import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.model.ParentStoryId;
|
||||||
import org.thoughtcrime.securesms.database.model.StoryType;
|
import org.thoughtcrime.securesms.database.model.StoryType;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2;
|
import org.thoughtcrime.securesms.notifications.v2.MessageNotifierV2;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
|
@ -52,6 +54,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
public static final String RECIPIENT_EXTRA = "recipient_extra";
|
public static final String RECIPIENT_EXTRA = "recipient_extra";
|
||||||
public static final String REPLY_METHOD = "reply_method";
|
public static final String REPLY_METHOD = "reply_method";
|
||||||
public static final String EARLIEST_TIMESTAMP = "earliest_timestamp";
|
public static final String EARLIEST_TIMESTAMP = "earliest_timestamp";
|
||||||
|
public static final String GROUP_STORY_ID_EXTRA = "group_story_id_extra";
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,6 +68,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
final RecipientId recipientId = intent.getParcelableExtra(RECIPIENT_EXTRA);
|
final RecipientId recipientId = intent.getParcelableExtra(RECIPIENT_EXTRA);
|
||||||
final ReplyMethod replyMethod = (ReplyMethod) intent.getSerializableExtra(REPLY_METHOD);
|
final ReplyMethod replyMethod = (ReplyMethod) intent.getSerializableExtra(REPLY_METHOD);
|
||||||
final CharSequence responseText = remoteInput.getCharSequence(MessageNotifierV2.EXTRA_REMOTE_REPLY);
|
final CharSequence responseText = remoteInput.getCharSequence(MessageNotifierV2.EXTRA_REMOTE_REPLY);
|
||||||
|
final long groupStoryId = intent.getLongExtra(GROUP_STORY_ID_EXTRA, Long.MIN_VALUE);
|
||||||
|
|
||||||
if (recipientId == null) throw new AssertionError("No recipientId specified");
|
if (recipientId == null) throw new AssertionError("No recipientId specified");
|
||||||
if (replyMethod == null) throw new AssertionError("No reply method specified");
|
if (replyMethod == null) throw new AssertionError("No reply method specified");
|
||||||
|
@ -76,6 +80,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
int subscriptionId = recipient.getDefaultSubscriptionId().orElse(-1);
|
int subscriptionId = recipient.getDefaultSubscriptionId().orElse(-1);
|
||||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
||||||
|
ParentStoryId parentStoryId = groupStoryId != Long.MIN_VALUE ? ParentStoryId.deserialize(groupStoryId) : null;
|
||||||
|
|
||||||
switch (replyMethod) {
|
switch (replyMethod) {
|
||||||
case GroupMessage: {
|
case GroupMessage: {
|
||||||
|
@ -88,7 +93,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
StoryType.NONE,
|
StoryType.NONE,
|
||||||
null,
|
parentStoryId,
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
|
@ -114,7 +119,9 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
throw new AssertionError("Unknown Reply method");
|
throw new AssertionError("Unknown Reply method");
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier().addStickyThread(threadId, intent.getLongExtra(EARLIEST_TIMESTAMP, System.currentTimeMillis()));
|
ApplicationDependencies.getMessageNotifier()
|
||||||
|
.addStickyThread(new NotificationThread(threadId, groupStoryId != Long.MIN_VALUE ? groupStoryId : null),
|
||||||
|
intent.getLongExtra(EARLIEST_TIMESTAMP, System.currentTimeMillis()));
|
||||||
|
|
||||||
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setRead(threadId, true);
|
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setRead(threadId, true);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.service.notification.StatusBarNotification
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import me.leolin.shortcutbadger.ShortcutBadger
|
import me.leolin.shortcutbadger.ShortcutBadger
|
||||||
|
import org.signal.core.util.PendingIntentFlags
|
||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase
|
import org.thoughtcrime.securesms.database.MessageDatabase
|
||||||
|
@ -31,6 +32,7 @@ import org.thoughtcrime.securesms.util.BubbleUtil.BubbleState
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||||
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder
|
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder
|
||||||
import org.whispersystems.signalservice.internal.util.Util
|
import org.whispersystems.signalservice.internal.util.Util
|
||||||
|
import java.util.Optional
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
@ -43,7 +45,7 @@ import kotlin.math.max
|
||||||
* MessageNotifier implementation using the new system for creating and showing notifications.
|
* MessageNotifier implementation using the new system for creating and showing notifications.
|
||||||
*/
|
*/
|
||||||
class MessageNotifierV2(context: Application) : MessageNotifier {
|
class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
@Volatile private var visibleThread: Long = -1
|
@Volatile private var visibleThread: NotificationThread? = null
|
||||||
@Volatile private var lastDesktopActivityTimestamp: Long = -1
|
@Volatile private var lastDesktopActivityTimestamp: Long = -1
|
||||||
@Volatile private var lastAudibleNotification: Long = -1
|
@Volatile private var lastAudibleNotification: Long = -1
|
||||||
@Volatile private var lastScheduledReminder: Long = 0
|
@Volatile private var lastScheduledReminder: Long = 0
|
||||||
|
@ -51,34 +53,34 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
@Volatile private var previousPrivacyPreference: NotificationPrivacyPreference = SignalStore.settings().messageNotificationsPrivacy
|
@Volatile private var previousPrivacyPreference: NotificationPrivacyPreference = SignalStore.settings().messageNotificationsPrivacy
|
||||||
@Volatile private var previousState: NotificationStateV2 = NotificationStateV2.EMPTY
|
@Volatile private var previousState: NotificationStateV2 = NotificationStateV2.EMPTY
|
||||||
|
|
||||||
private val threadReminders: MutableMap<Long, Reminder> = ConcurrentHashMap()
|
private val threadReminders: MutableMap<NotificationThread, Reminder> = ConcurrentHashMap()
|
||||||
private val stickyThreads: MutableMap<Long, StickyThread> = mutableMapOf()
|
private val stickyThreads: MutableMap<NotificationThread, StickyThread> = mutableMapOf()
|
||||||
|
|
||||||
private val executor = CancelableExecutor()
|
private val executor = CancelableExecutor()
|
||||||
|
|
||||||
override fun setVisibleThread(threadId: Long) {
|
override fun setVisibleThread(notificationThread: NotificationThread?) {
|
||||||
visibleThread = threadId
|
visibleThread = notificationThread
|
||||||
stickyThreads.remove(threadId)
|
stickyThreads.remove(notificationThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getVisibleThread(): Long {
|
override fun getVisibleThread(): Optional<NotificationThread> {
|
||||||
return visibleThread
|
return Optional.ofNullable(visibleThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearVisibleThread() {
|
override fun clearVisibleThread() {
|
||||||
setVisibleThread(-1)
|
setVisibleThread(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setLastDesktopActivityTimestamp(timestamp: Long) {
|
override fun setLastDesktopActivityTimestamp(timestamp: Long) {
|
||||||
lastDesktopActivityTimestamp = timestamp
|
lastDesktopActivityTimestamp = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyMessageDeliveryFailed(context: Context, recipient: Recipient, threadId: Long) {
|
override fun notifyMessageDeliveryFailed(context: Context, recipient: Recipient, notificationThread: NotificationThread) {
|
||||||
NotificationFactory.notifyMessageDeliveryFailed(context, recipient, threadId, visibleThread)
|
NotificationFactory.notifyMessageDeliveryFailed(context, recipient, notificationThread, visibleThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyProofRequired(context: Context, recipient: Recipient, threadId: Long) {
|
override fun notifyProofRequired(context: Context, recipient: Recipient, notificationThread: NotificationThread) {
|
||||||
NotificationFactory.notifyProofRequired(context, recipient, threadId, visibleThread)
|
NotificationFactory.notifyProofRequired(context, recipient, notificationThread, visibleThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancelDelayedNotifications() {
|
override fun cancelDelayedNotifications() {
|
||||||
|
@ -86,24 +88,24 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNotification(context: Context) {
|
override fun updateNotification(context: Context) {
|
||||||
updateNotification(context, -1, false, 0, BubbleState.HIDDEN)
|
updateNotification(context, null, false, 0, BubbleState.HIDDEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNotification(context: Context, threadId: Long) {
|
override fun updateNotification(context: Context, notificationThread: NotificationThread) {
|
||||||
if (System.currentTimeMillis() - lastDesktopActivityTimestamp < DESKTOP_ACTIVITY_PERIOD) {
|
if (System.currentTimeMillis() - lastDesktopActivityTimestamp < DESKTOP_ACTIVITY_PERIOD) {
|
||||||
Log.i(TAG, "Scheduling delayed notification...")
|
Log.i(TAG, "Scheduling delayed notification...")
|
||||||
executor.enqueue(context, threadId)
|
executor.enqueue(context, notificationThread)
|
||||||
} else {
|
} else {
|
||||||
updateNotification(context, threadId, true)
|
updateNotification(context, notificationThread, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNotification(context: Context, threadId: Long, defaultBubbleState: BubbleState) {
|
override fun updateNotification(context: Context, notificationThread: NotificationThread, defaultBubbleState: BubbleState) {
|
||||||
updateNotification(context, threadId, false, 0, defaultBubbleState)
|
updateNotification(context, notificationThread, false, 0, defaultBubbleState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNotification(context: Context, threadId: Long, signal: Boolean) {
|
override fun updateNotification(context: Context, notificationThread: NotificationThread, signal: Boolean) {
|
||||||
updateNotification(context, threadId, signal, 0, BubbleState.HIDDEN)
|
updateNotification(context, notificationThread, signal, 0, BubbleState.HIDDEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,7 +114,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
*/
|
*/
|
||||||
override fun updateNotification(
|
override fun updateNotification(
|
||||||
context: Context,
|
context: Context,
|
||||||
threadId: Long,
|
notificationThread: NotificationThread?,
|
||||||
signal: Boolean,
|
signal: Boolean,
|
||||||
reminderCount: Int,
|
reminderCount: Int,
|
||||||
defaultBubbleState: BubbleState
|
defaultBubbleState: BubbleState
|
||||||
|
@ -162,22 +164,22 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
|
|
||||||
val displayedNotifications: Set<Int>? = ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrNull()
|
val displayedNotifications: Set<Int>? = ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrNull()
|
||||||
if (displayedNotifications != null) {
|
if (displayedNotifications != null) {
|
||||||
val cleanedUpThreadIds: MutableSet<Long> = mutableSetOf()
|
val cleanedUpThreads: MutableSet<NotificationThread> = mutableSetOf()
|
||||||
state.conversations.filterNot { it.hasNewNotifications() || displayedNotifications.contains(it.notificationId) }
|
state.conversations.filterNot { it.hasNewNotifications() || displayedNotifications.contains(it.notificationId) }
|
||||||
.forEach { conversation ->
|
.forEach { conversation ->
|
||||||
cleanedUpThreadIds += conversation.threadId
|
cleanedUpThreads += conversation.thread
|
||||||
conversation.notificationItems.forEach { item ->
|
conversation.notificationItems.forEach { item ->
|
||||||
val messageDatabase: MessageDatabase = if (item.isMms) SignalDatabase.mms else SignalDatabase.sms
|
val messageDatabase: MessageDatabase = if (item.isMms) SignalDatabase.mms else SignalDatabase.sms
|
||||||
messageDatabase.markAsNotified(item.id)
|
messageDatabase.markAsNotified(item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cleanedUpThreadIds.isNotEmpty()) {
|
if (cleanedUpThreads.isNotEmpty()) {
|
||||||
Log.i(TAG, "Cleaned up ${cleanedUpThreadIds.size} thread(s) with dangling notifications")
|
Log.i(TAG, "Cleaned up ${cleanedUpThreads.size} thread(s) with dangling notifications")
|
||||||
state = state.copy(conversations = state.conversations.filterNot { cleanedUpThreadIds.contains(it.threadId) })
|
state = state.copy(conversations = state.conversations.filterNot { cleanedUpThreads.contains(it.thread) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val retainStickyThreadIds: Set<Long> = state.getThreadsWithMostRecentNotificationFromSelf()
|
val retainStickyThreadIds: Set<NotificationThread> = state.getThreadsWithMostRecentNotificationFromSelf()
|
||||||
stickyThreads.keys.retainAll { retainStickyThreadIds.contains(it) }
|
stickyThreads.keys.retainAll { retainStickyThreadIds.contains(it) }
|
||||||
|
|
||||||
if (state.isEmpty) {
|
if (state.isEmpty) {
|
||||||
|
@ -188,13 +190,13 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val alertOverrides: Set<Long> = threadReminders.filter { (_, reminder) -> reminder.lastNotified < System.currentTimeMillis() - REMINDER_TIMEOUT }.keys
|
val alertOverrides: Set<NotificationThread> = threadReminders.filter { (_, reminder) -> reminder.lastNotified < System.currentTimeMillis() - REMINDER_TIMEOUT }.keys
|
||||||
|
|
||||||
val threadsThatAlerted: Set<Long> = NotificationFactory.notify(
|
val threadsThatAlerted: Set<NotificationThread> = NotificationFactory.notify(
|
||||||
context = ContextThemeWrapper(context, R.style.TextSecure_LightTheme),
|
context = ContextThemeWrapper(context, R.style.TextSecure_LightTheme),
|
||||||
state = state,
|
state = state,
|
||||||
visibleThreadId = visibleThread,
|
visibleThread = visibleThread,
|
||||||
targetThreadId = threadId,
|
targetThread = notificationThread,
|
||||||
defaultBubbleState = defaultBubbleState,
|
defaultBubbleState = defaultBubbleState,
|
||||||
lastAudibleNotification = lastAudibleNotification,
|
lastAudibleNotification = lastAudibleNotification,
|
||||||
notificationConfigurationChanged = notificationConfigurationChanged,
|
notificationConfigurationChanged = notificationConfigurationChanged,
|
||||||
|
@ -224,7 +226,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
Log.i(TAG, "threads: ${state.threadCount} messages: ${state.messageCount}")
|
Log.i(TAG, "threads: ${state.threadCount} messages: ${state.messageCount}")
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
if (Build.VERSION.SDK_INT >= 24) {
|
||||||
val ids = state.conversations.filter { it.threadId != visibleThread }.map { it.notificationId } + stickyThreads.map { (_, stickyThread) -> stickyThread.notificationId }
|
val ids = state.conversations.filter { it.thread != visibleThread }.map { it.notificationId } + stickyThreads.map { (_, stickyThread) -> stickyThread.notificationId }
|
||||||
val notShown = ids - ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrDefault(emptySet())
|
val notShown = ids - ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrDefault(emptySet())
|
||||||
if (notShown.isNotEmpty()) {
|
if (notShown.isNotEmpty()) {
|
||||||
Log.e(TAG, "Notifications should be showing but are not for ${notShown.size} threads")
|
Log.e(TAG, "Notifications should be showing but are not for ${notShown.size} threads")
|
||||||
|
@ -236,23 +238,23 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
// Intentionally left blank
|
// Intentionally left blank
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addStickyThread(threadId: Long, earliestTimestamp: Long) {
|
override fun addStickyThread(notificationThread: NotificationThread, earliestTimestamp: Long) {
|
||||||
stickyThreads[threadId] = StickyThread(threadId, NotificationIds.getNotificationIdForThread(threadId), earliestTimestamp)
|
stickyThreads[notificationThread] = StickyThread(notificationThread, NotificationIds.getNotificationIdForThread(notificationThread), earliestTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeStickyThread(threadId: Long) {
|
override fun removeStickyThread(notificationThread: NotificationThread) {
|
||||||
stickyThreads.remove(threadId)
|
stickyThreads.remove(notificationThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateReminderTimestamps(context: Context, alertOverrides: Set<Long>, threadsThatAlerted: Set<Long>) {
|
private fun updateReminderTimestamps(context: Context, alertOverrides: Set<NotificationThread>, threadsThatAlerted: Set<NotificationThread>) {
|
||||||
if (SignalStore.settings().messageNotificationsRepeatAlerts == 0) {
|
if (SignalStore.settings().messageNotificationsRepeatAlerts == 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val iterator: MutableIterator<MutableEntry<Long, Reminder>> = threadReminders.iterator()
|
val iterator: MutableIterator<MutableEntry<NotificationThread, Reminder>> = threadReminders.iterator()
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val entry: MutableEntry<Long, Reminder> = iterator.next()
|
val entry: MutableEntry<NotificationThread, Reminder> = iterator.next()
|
||||||
val (id: Long, reminder: Reminder) = entry
|
val (id: NotificationThread, reminder: Reminder) = entry
|
||||||
if (alertOverrides.contains(id)) {
|
if (alertOverrides.contains(id)) {
|
||||||
val notifyCount: Int = reminder.count + 1
|
val notifyCount: Int = reminder.count + 1
|
||||||
if (notifyCount >= SignalStore.settings().messageNotificationsRepeatAlerts) {
|
if (notifyCount >= SignalStore.settings().messageNotificationsRepeatAlerts) {
|
||||||
|
@ -263,7 +265,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (alertedThreadId: Long in threadsThatAlerted) {
|
for (alertedThreadId: NotificationThread in threadsThatAlerted) {
|
||||||
threadReminders[alertedThreadId] = Reminder(lastAudibleNotification)
|
threadReminders[alertedThreadId] = Reminder(lastAudibleNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +284,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
val alarmManager: AlarmManager? = ContextCompat.getSystemService(context, AlarmManager::class.java)
|
val alarmManager: AlarmManager? = ContextCompat.getSystemService(context, AlarmManager::class.java)
|
||||||
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
|
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntentFlags.updateCurrent())
|
||||||
alarmManager?.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent)
|
alarmManager?.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent)
|
||||||
lastScheduledReminder = System.currentTimeMillis()
|
lastScheduledReminder = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
|
@ -291,7 +293,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
lastScheduledReminder = 0
|
lastScheduledReminder = 0
|
||||||
threadReminders.clear()
|
threadReminders.clear()
|
||||||
|
|
||||||
val pendingIntent: PendingIntent? = PendingIntent.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntent.FLAG_CANCEL_CURRENT)
|
val pendingIntent: PendingIntent? = PendingIntent.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntentFlags.cancelCurrent())
|
||||||
if (pendingIntent != null) {
|
if (pendingIntent != null) {
|
||||||
val alarmManager: AlarmManager? = ContextCompat.getSystemService(context, AlarmManager::class.java)
|
val alarmManager: AlarmManager? = ContextCompat.getSystemService(context, AlarmManager::class.java)
|
||||||
alarmManager?.cancel(pendingIntent)
|
alarmManager?.cancel(pendingIntent)
|
||||||
|
@ -317,7 +319,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StickyThread(val threadId: Long, val notificationId: Int, val earliestTimestamp: Long)
|
data class StickyThread(val notificationThread: NotificationThread, val notificationId: Int, val earliestTimestamp: Long)
|
||||||
private data class Reminder(val lastNotified: Long, val count: Int = 0)
|
private data class Reminder(val lastNotified: Long, val count: Int = 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,8 +368,8 @@ private class CancelableExecutor {
|
||||||
private val executor: Executor = Executors.newSingleThreadExecutor()
|
private val executor: Executor = Executors.newSingleThreadExecutor()
|
||||||
private val tasks: MutableSet<DelayedNotification> = mutableSetOf()
|
private val tasks: MutableSet<DelayedNotification> = mutableSetOf()
|
||||||
|
|
||||||
fun enqueue(context: Context, threadId: Long) {
|
fun enqueue(context: Context, notificationThread: NotificationThread) {
|
||||||
execute(DelayedNotification(context, threadId))
|
execute(DelayedNotification(context, notificationThread))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun execute(runnable: DelayedNotification) {
|
private fun execute(runnable: DelayedNotification) {
|
||||||
|
@ -387,7 +389,7 @@ private class CancelableExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DelayedNotification constructor(private val context: Context, private val threadId: Long) : Runnable {
|
private class DelayedNotification constructor(private val context: Context, private val thread: NotificationThread) : Runnable {
|
||||||
private val canceled = AtomicBoolean(false)
|
private val canceled = AtomicBoolean(false)
|
||||||
private val delayUntil: Long = System.currentTimeMillis() + DELAY
|
private val delayUntil: Long = System.currentTimeMillis() + DELAY
|
||||||
|
|
||||||
|
@ -399,7 +401,7 @@ private class CancelableExecutor {
|
||||||
}
|
}
|
||||||
if (!canceled.get()) {
|
if (!canceled.get()) {
|
||||||
Log.i(TAG, "Not canceled, notifying...")
|
Log.i(TAG, "Not canceled, notifying...")
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId, true)
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, thread, true)
|
||||||
ApplicationDependencies.getMessageNotifier().cancelDelayedNotifications()
|
ApplicationDependencies.getMessageNotifier().cancelDelayedNotifications()
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Canceled, not notifying...")
|
Log.w(TAG, "Canceled, not notifying...")
|
||||||
|
|
|
@ -334,7 +334,7 @@ sealed class NotificationBuilder(protected val context: Context) {
|
||||||
val intent = PendingIntent.getActivity(
|
val intent = PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
0,
|
0,
|
||||||
ConversationIntents.createBubbleIntent(context, conversation.recipient.id, conversation.threadId),
|
ConversationIntents.createBubbleIntent(context, conversation.recipient.id, conversation.thread.threadId),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.notifications.ReplyMethod
|
||||||
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference
|
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||||
|
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
||||||
import org.thoughtcrime.securesms.util.Util
|
import org.thoughtcrime.securesms.util.Util
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,11 +31,11 @@ import org.thoughtcrime.securesms.util.Util
|
||||||
*/
|
*/
|
||||||
data class NotificationConversation(
|
data class NotificationConversation(
|
||||||
val recipient: Recipient,
|
val recipient: Recipient,
|
||||||
val threadId: Long,
|
val thread: NotificationThread,
|
||||||
val notificationItems: List<NotificationItemV2>
|
val notificationItems: List<NotificationItemV2>
|
||||||
) {
|
) {
|
||||||
val mostRecentNotification: NotificationItemV2 = notificationItems.last()
|
val mostRecentNotification: NotificationItemV2 = notificationItems.last()
|
||||||
val notificationId: Int = NotificationIds.getNotificationIdForThread(threadId)
|
val notificationId: Int = NotificationIds.getNotificationIdForThread(thread)
|
||||||
val sortKey: Long = Long.MAX_VALUE - mostRecentNotification.timestamp
|
val sortKey: Long = Long.MAX_VALUE - mostRecentNotification.timestamp
|
||||||
val messageCount: Int = notificationItems.size
|
val messageCount: Int = notificationItems.size
|
||||||
val isGroup: Boolean = recipient.isGroup
|
val isGroup: Boolean = recipient.isGroup
|
||||||
|
@ -111,10 +112,14 @@ data class NotificationConversation(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPendingIntent(context: Context): PendingIntent {
|
fun getPendingIntent(context: Context): PendingIntent {
|
||||||
val intent: Intent = ConversationIntents.createBuilder(context, recipient.id, threadId)
|
val intent: Intent = if (thread.groupStoryId != null) {
|
||||||
|
StoryViewerActivity.createIntent(context, recipient.id, thread.groupStoryId, recipient.shouldHideStory())
|
||||||
|
} else {
|
||||||
|
ConversationIntents.createBuilder(context, recipient.id, thread.threadId)
|
||||||
.withStartingPosition(mostRecentNotification.getStartingPosition(context))
|
.withStartingPosition(mostRecentNotification.getStartingPosition(context))
|
||||||
.build()
|
.build()
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
}
|
||||||
|
|
||||||
return TaskStackBuilder.create(context)
|
return TaskStackBuilder.create(context)
|
||||||
.addNextIntentWithParentStack(intent)
|
.addNextIntentWithParentStack(intent)
|
||||||
|
@ -133,7 +138,7 @@ data class NotificationConversation(
|
||||||
.setAction(DeleteNotificationReceiver.DELETE_NOTIFICATION_ACTION)
|
.setAction(DeleteNotificationReceiver.DELETE_NOTIFICATION_ACTION)
|
||||||
.putExtra(DeleteNotificationReceiver.EXTRA_IDS, ids)
|
.putExtra(DeleteNotificationReceiver.EXTRA_IDS, ids)
|
||||||
.putExtra(DeleteNotificationReceiver.EXTRA_MMS, mms)
|
.putExtra(DeleteNotificationReceiver.EXTRA_MMS, mms)
|
||||||
.putExtra(DeleteNotificationReceiver.EXTRA_THREAD_IDS, longArrayOf(threadId))
|
.putParcelableArrayListExtra(DeleteNotificationReceiver.EXTRA_THREADS, arrayListOf(thread))
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
@ -142,19 +147,19 @@ data class NotificationConversation(
|
||||||
fun getMarkAsReadIntent(context: Context): PendingIntent {
|
fun getMarkAsReadIntent(context: Context): PendingIntent {
|
||||||
val intent = Intent(context, MarkReadReceiver::class.java)
|
val intent = Intent(context, MarkReadReceiver::class.java)
|
||||||
.setAction(MarkReadReceiver.CLEAR_ACTION)
|
.setAction(MarkReadReceiver.CLEAR_ACTION)
|
||||||
.putExtra(MarkReadReceiver.THREAD_IDS_EXTRA, longArrayOf(mostRecentNotification.threadId))
|
.putParcelableArrayListExtra(MarkReadReceiver.THREADS_EXTRA, arrayListOf(mostRecentNotification.thread))
|
||||||
.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, notificationId)
|
.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, notificationId)
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(context, (threadId * 2).toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, (thread.threadId * 2).toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getQuickReplyIntent(context: Context): PendingIntent {
|
fun getQuickReplyIntent(context: Context): PendingIntent {
|
||||||
val intent: Intent = ConversationIntents.createPopUpBuilder(context, recipient.id, mostRecentNotification.threadId)
|
val intent: Intent = ConversationIntents.createPopUpBuilder(context, recipient.id, mostRecentNotification.thread.threadId)
|
||||||
.build()
|
.build()
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
|
||||||
return PendingIntent.getActivity(context, (threadId * 2).toInt() + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getActivity(context, (thread.threadId * 2).toInt() + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRemoteReplyIntent(context: Context, replyMethod: ReplyMethod): PendingIntent {
|
fun getRemoteReplyIntent(context: Context, replyMethod: ReplyMethod): PendingIntent {
|
||||||
|
@ -163,21 +168,22 @@ data class NotificationConversation(
|
||||||
.putExtra(RemoteReplyReceiver.RECIPIENT_EXTRA, recipient.id)
|
.putExtra(RemoteReplyReceiver.RECIPIENT_EXTRA, recipient.id)
|
||||||
.putExtra(RemoteReplyReceiver.REPLY_METHOD, replyMethod)
|
.putExtra(RemoteReplyReceiver.REPLY_METHOD, replyMethod)
|
||||||
.putExtra(RemoteReplyReceiver.EARLIEST_TIMESTAMP, notificationItems.first().timestamp)
|
.putExtra(RemoteReplyReceiver.EARLIEST_TIMESTAMP, notificationItems.first().timestamp)
|
||||||
|
.putExtra(RemoteReplyReceiver.GROUP_STORY_ID_EXTRA, notificationItems.first().thread.groupStoryId ?: Long.MIN_VALUE)
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(context, (threadId * 2).toInt() + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, (thread.threadId * 2).toInt() + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTurnOffJoinedNotificationsIntent(context: Context): PendingIntent {
|
fun getTurnOffJoinedNotificationsIntent(context: Context): PendingIntent {
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
0,
|
0,
|
||||||
TurnOffContactJoinedNotificationsActivity.newIntent(context, threadId),
|
TurnOffContactJoinedNotificationsActivity.newIntent(context, thread.threadId),
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "NotificationConversation(threadId=$threadId, notificationItems=$notificationItems, messageCount=$messageCount, hasNewNotifications=${hasNewNotifications()})"
|
return "NotificationConversation(thread=$thread, notificationItems=$notificationItems, messageCount=$messageCount, hasNewNotifications=${hasNewNotifications()})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationIds
|
import org.thoughtcrime.securesms.notifications.NotificationIds
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.stories.my.MyStoriesActivity
|
import org.thoughtcrime.securesms.stories.my.MyStoriesActivity
|
||||||
|
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
||||||
import org.thoughtcrime.securesms.util.BubbleUtil
|
import org.thoughtcrime.securesms.util.BubbleUtil
|
||||||
import org.thoughtcrime.securesms.util.ConversationUtil
|
import org.thoughtcrime.securesms.util.ConversationUtil
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||||
|
@ -42,26 +43,26 @@ object NotificationFactory {
|
||||||
fun notify(
|
fun notify(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: NotificationStateV2,
|
state: NotificationStateV2,
|
||||||
visibleThreadId: Long,
|
visibleThread: NotificationThread?,
|
||||||
targetThreadId: Long,
|
targetThread: NotificationThread?,
|
||||||
defaultBubbleState: BubbleUtil.BubbleState,
|
defaultBubbleState: BubbleUtil.BubbleState,
|
||||||
lastAudibleNotification: Long,
|
lastAudibleNotification: Long,
|
||||||
notificationConfigurationChanged: Boolean,
|
notificationConfigurationChanged: Boolean,
|
||||||
alertOverrides: Set<Long>,
|
alertOverrides: Set<NotificationThread>,
|
||||||
previousState: NotificationStateV2
|
previousState: NotificationStateV2
|
||||||
): Set<Long> {
|
): Set<NotificationThread> {
|
||||||
if (state.isEmpty) {
|
if (state.isEmpty) {
|
||||||
Log.d(TAG, "State is empty, bailing")
|
Log.d(TAG, "State is empty, bailing")
|
||||||
return emptySet()
|
return emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
val nonVisibleThreadCount: Int = state.conversations.count { it.threadId != visibleThreadId }
|
val nonVisibleThreadCount: Int = state.conversations.count { it.thread != visibleThread }
|
||||||
return if (Build.VERSION.SDK_INT < 24) {
|
return if (Build.VERSION.SDK_INT < 24) {
|
||||||
notify19(
|
notify19(
|
||||||
context = context,
|
context = context,
|
||||||
state = state,
|
state = state,
|
||||||
visibleThreadId = visibleThreadId,
|
visibleThread = visibleThread,
|
||||||
targetThreadId = targetThreadId,
|
targetThread = targetThread,
|
||||||
defaultBubbleState = defaultBubbleState,
|
defaultBubbleState = defaultBubbleState,
|
||||||
lastAudibleNotification = lastAudibleNotification,
|
lastAudibleNotification = lastAudibleNotification,
|
||||||
alertOverrides = alertOverrides,
|
alertOverrides = alertOverrides,
|
||||||
|
@ -71,8 +72,8 @@ object NotificationFactory {
|
||||||
notify24(
|
notify24(
|
||||||
context = context,
|
context = context,
|
||||||
state = state,
|
state = state,
|
||||||
visibleThreadId = visibleThreadId,
|
visibleThread = visibleThread,
|
||||||
targetThreadId = targetThreadId,
|
targetThread = targetThread,
|
||||||
defaultBubbleState = defaultBubbleState,
|
defaultBubbleState = defaultBubbleState,
|
||||||
lastAudibleNotification = lastAudibleNotification,
|
lastAudibleNotification = lastAudibleNotification,
|
||||||
notificationConfigurationChanged = notificationConfigurationChanged,
|
notificationConfigurationChanged = notificationConfigurationChanged,
|
||||||
|
@ -86,16 +87,16 @@ object NotificationFactory {
|
||||||
private fun notify19(
|
private fun notify19(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: NotificationStateV2,
|
state: NotificationStateV2,
|
||||||
visibleThreadId: Long,
|
visibleThread: NotificationThread?,
|
||||||
targetThreadId: Long,
|
targetThread: NotificationThread?,
|
||||||
defaultBubbleState: BubbleUtil.BubbleState,
|
defaultBubbleState: BubbleUtil.BubbleState,
|
||||||
lastAudibleNotification: Long,
|
lastAudibleNotification: Long,
|
||||||
alertOverrides: Set<Long>,
|
alertOverrides: Set<NotificationThread>,
|
||||||
nonVisibleThreadCount: Int
|
nonVisibleThreadCount: Int
|
||||||
): Set<Long> {
|
): Set<NotificationThread> {
|
||||||
val threadsThatNewlyAlerted: MutableSet<Long> = mutableSetOf()
|
val threadsThatNewlyAlerted: MutableSet<NotificationThread> = mutableSetOf()
|
||||||
|
|
||||||
state.conversations.find { it.threadId == visibleThreadId }?.let { conversation ->
|
state.conversations.find { it.thread == visibleThread }?.let { conversation ->
|
||||||
if (conversation.hasNewNotifications()) {
|
if (conversation.hasNewNotifications()) {
|
||||||
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
||||||
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
||||||
|
@ -103,21 +104,21 @@ object NotificationFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonVisibleThreadCount == 1) {
|
if (nonVisibleThreadCount == 1) {
|
||||||
state.conversations.first { it.threadId != visibleThreadId }.let { conversation ->
|
state.conversations.first { it.thread != visibleThread }.let { conversation ->
|
||||||
notifyForConversation(
|
notifyForConversation(
|
||||||
context = context,
|
context = context,
|
||||||
conversation = conversation,
|
conversation = conversation,
|
||||||
targetThreadId = targetThreadId,
|
targetThread = targetThread,
|
||||||
defaultBubbleState = defaultBubbleState,
|
defaultBubbleState = defaultBubbleState,
|
||||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.thread)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
||||||
)
|
)
|
||||||
if (conversation.hasNewNotifications()) {
|
if (conversation.hasNewNotifications()) {
|
||||||
threadsThatNewlyAlerted += conversation.threadId
|
threadsThatNewlyAlerted += conversation.thread
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (nonVisibleThreadCount > 1) {
|
} else if (nonVisibleThreadCount > 1) {
|
||||||
val nonVisibleConversations: List<NotificationConversation> = state.getNonVisibleConversation(visibleThreadId)
|
val nonVisibleConversations: List<NotificationConversation> = state.getNonVisibleConversation(visibleThread)
|
||||||
threadsThatNewlyAlerted += nonVisibleConversations.filter { it.hasNewNotifications() }.map { it.threadId }
|
threadsThatNewlyAlerted += nonVisibleConversations.filter { it.hasNewNotifications() }.map { it.thread }
|
||||||
notifySummary(context = context, state = state.copy(conversations = nonVisibleConversations))
|
notifySummary(context = context, state = state.copy(conversations = nonVisibleConversations))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,38 +129,38 @@ object NotificationFactory {
|
||||||
private fun notify24(
|
private fun notify24(
|
||||||
context: Context,
|
context: Context,
|
||||||
state: NotificationStateV2,
|
state: NotificationStateV2,
|
||||||
visibleThreadId: Long,
|
visibleThread: NotificationThread?,
|
||||||
targetThreadId: Long,
|
targetThread: NotificationThread?,
|
||||||
defaultBubbleState: BubbleUtil.BubbleState,
|
defaultBubbleState: BubbleUtil.BubbleState,
|
||||||
lastAudibleNotification: Long,
|
lastAudibleNotification: Long,
|
||||||
notificationConfigurationChanged: Boolean,
|
notificationConfigurationChanged: Boolean,
|
||||||
alertOverrides: Set<Long>,
|
alertOverrides: Set<NotificationThread>,
|
||||||
nonVisibleThreadCount: Int,
|
nonVisibleThreadCount: Int,
|
||||||
previousState: NotificationStateV2
|
previousState: NotificationStateV2
|
||||||
): Set<Long> {
|
): Set<NotificationThread> {
|
||||||
val threadsThatNewlyAlerted: MutableSet<Long> = mutableSetOf()
|
val threadsThatNewlyAlerted: MutableSet<NotificationThread> = mutableSetOf()
|
||||||
|
|
||||||
state.conversations.forEach { conversation ->
|
state.conversations.forEach { conversation ->
|
||||||
if (conversation.threadId == visibleThreadId && conversation.hasNewNotifications()) {
|
if (conversation.thread == visibleThread && conversation.hasNewNotifications()) {
|
||||||
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
||||||
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
||||||
} else if (notificationConfigurationChanged || conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId) || !conversation.hasSameContent(previousState.getConversation(conversation.threadId))) {
|
} else if (notificationConfigurationChanged || conversation.hasNewNotifications() || alertOverrides.contains(conversation.thread) || !conversation.hasSameContent(previousState.getConversation(conversation.thread))) {
|
||||||
if (conversation.hasNewNotifications()) {
|
if (conversation.hasNewNotifications()) {
|
||||||
threadsThatNewlyAlerted += conversation.threadId
|
threadsThatNewlyAlerted += conversation.thread
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyForConversation(
|
notifyForConversation(
|
||||||
context = context,
|
context = context,
|
||||||
conversation = conversation,
|
conversation = conversation,
|
||||||
targetThreadId = targetThreadId,
|
targetThread = targetThread,
|
||||||
defaultBubbleState = defaultBubbleState,
|
defaultBubbleState = defaultBubbleState,
|
||||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.thread)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonVisibleThreadCount > 1 || ServiceUtil.getNotificationManager(context).isDisplayingSummaryNotification()) {
|
if (nonVisibleThreadCount > 1 || ServiceUtil.getNotificationManager(context).isDisplayingSummaryNotification()) {
|
||||||
notifySummary(context = context, state = state.copy(conversations = state.getNonVisibleConversation(visibleThreadId)))
|
notifySummary(context = context, state = state.copy(conversations = state.getNonVisibleConversation(visibleThread)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return threadsThatNewlyAlerted
|
return threadsThatNewlyAlerted
|
||||||
|
@ -168,7 +169,7 @@ object NotificationFactory {
|
||||||
private fun notifyForConversation(
|
private fun notifyForConversation(
|
||||||
context: Context,
|
context: Context,
|
||||||
conversation: NotificationConversation,
|
conversation: NotificationConversation,
|
||||||
targetThreadId: Long,
|
targetThread: NotificationThread?,
|
||||||
defaultBubbleState: BubbleUtil.BubbleState,
|
defaultBubbleState: BubbleUtil.BubbleState,
|
||||||
shouldAlert: Boolean
|
shouldAlert: Boolean
|
||||||
) {
|
) {
|
||||||
|
@ -204,7 +205,7 @@ object NotificationFactory {
|
||||||
setLights()
|
setLights()
|
||||||
setAlarms(conversation.recipient)
|
setAlarms(conversation.recipient)
|
||||||
setTicker(conversation.mostRecentNotification.getStyledPrimaryText(context, true))
|
setTicker(conversation.mostRecentNotification.getStyledPrimaryText(context, true))
|
||||||
setBubbleMetadata(conversation, if (targetThreadId == conversation.threadId) defaultBubbleState else BubbleUtil.BubbleState.HIDDEN)
|
setBubbleMetadata(conversation, if (targetThread == conversation.thread) defaultBubbleState else BubbleUtil.BubbleState.HIDDEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversation.isOnlyContactJoinedEvent) {
|
if (conversation.isOnlyContactJoinedEvent) {
|
||||||
|
@ -291,8 +292,8 @@ object NotificationFactory {
|
||||||
ringtone.play()
|
ringtone.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyMessageDeliveryFailed(context: Context, recipient: Recipient, threadId: Long, visibleThread: Long) {
|
fun notifyMessageDeliveryFailed(context: Context, recipient: Recipient, thread: NotificationThread, visibleThread: NotificationThread?) {
|
||||||
if (threadId == visibleThread) {
|
if (thread == visibleThread) {
|
||||||
notifyInThread(context, recipient, 0)
|
notifyInThread(context, recipient, 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -300,8 +301,10 @@ object NotificationFactory {
|
||||||
val intent: Intent = if (recipient.isDistributionList) {
|
val intent: Intent = if (recipient.isDistributionList) {
|
||||||
Intent(context, MyStoriesActivity::class.java)
|
Intent(context, MyStoriesActivity::class.java)
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
} else if (thread.groupStoryId != null) {
|
||||||
|
StoryViewerActivity.createIntent(context, recipient.id, thread.groupStoryId, recipient.shouldHideStory())
|
||||||
} else {
|
} else {
|
||||||
ConversationIntents.createBuilder(context, recipient.id, threadId)
|
ConversationIntents.createBuilder(context, recipient.id, thread.threadId)
|
||||||
.build()
|
.build()
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
}
|
}
|
||||||
|
@ -320,11 +323,11 @@ object NotificationFactory {
|
||||||
setChannelId(NotificationChannels.FAILURES)
|
setChannelId(NotificationChannels.FAILURES)
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManagerCompat.from(context).safelyNotify(context, recipient, threadId.toInt(), builder.build())
|
NotificationManagerCompat.from(context).safelyNotify(context, recipient, NotificationIds.getNotificationIdForMessageDeliveryFailed(thread), builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyProofRequired(context: Context, recipient: Recipient, threadId: Long, visibleThread: Long) {
|
fun notifyProofRequired(context: Context, recipient: Recipient, thread: NotificationThread, visibleThread: NotificationThread?) {
|
||||||
if (threadId == visibleThread) {
|
if (thread == visibleThread) {
|
||||||
notifyInThread(context, recipient, 0)
|
notifyInThread(context, recipient, 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -332,8 +335,10 @@ object NotificationFactory {
|
||||||
val intent: Intent = if (recipient.isDistributionList) {
|
val intent: Intent = if (recipient.isDistributionList) {
|
||||||
Intent(context, MyStoriesActivity::class.java)
|
Intent(context, MyStoriesActivity::class.java)
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
} else if (thread.groupStoryId != null) {
|
||||||
|
StoryViewerActivity.createIntent(context, recipient.id, thread.groupStoryId, recipient.shouldHideStory())
|
||||||
} else {
|
} else {
|
||||||
ConversationIntents.createBuilder(context, recipient.id, threadId)
|
ConversationIntents.createBuilder(context, recipient.id, thread.threadId)
|
||||||
.build()
|
.build()
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
}
|
}
|
||||||
|
@ -352,7 +357,7 @@ object NotificationFactory {
|
||||||
setChannelId(NotificationChannels.FAILURES)
|
setChannelId(NotificationChannels.FAILURES)
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManagerCompat.from(context).safelyNotify(context, recipient, threadId.toInt(), builder.build())
|
NotificationManagerCompat.from(context).safelyNotify(context, recipient, NotificationIds.getNotificationIdForMessageDeliveryFailed(thread), builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -361,7 +366,7 @@ object NotificationFactory {
|
||||||
|
|
||||||
val conversation = NotificationConversation(
|
val conversation = NotificationConversation(
|
||||||
recipient = recipient,
|
recipient = recipient,
|
||||||
threadId = threadId,
|
thread = NotificationThread.forConversation(threadId),
|
||||||
notificationItems = listOf(
|
notificationItems = listOf(
|
||||||
MessageNotification(
|
MessageNotification(
|
||||||
threadRecipient = recipient,
|
threadRecipient = recipient,
|
||||||
|
|
|
@ -40,7 +40,7 @@ private const val MAX_DISPLAY_LENGTH = 500
|
||||||
sealed class NotificationItemV2(val threadRecipient: Recipient, protected val record: MessageRecord) : Comparable<NotificationItemV2> {
|
sealed class NotificationItemV2(val threadRecipient: Recipient, protected val record: MessageRecord) : Comparable<NotificationItemV2> {
|
||||||
|
|
||||||
val id: Long = record.id
|
val id: Long = record.id
|
||||||
val threadId: Long = record.threadId
|
val thread = NotificationThread.fromMessageRecord(record)
|
||||||
val isMms: Boolean = record.isMms
|
val isMms: Boolean = record.isMms
|
||||||
val slideDeck: SlideDeck? = if (record.isViewOnce) null else (record as? MmsMessageRecord)?.slideDeck
|
val slideDeck: SlideDeck? = if (record.isViewOnce) null else (record as? MmsMessageRecord)?.slideDeck
|
||||||
val isJoined: Boolean = record.isJoined
|
val isJoined: Boolean = record.isJoined
|
||||||
|
@ -126,7 +126,7 @@ sealed class NotificationItemV2(val threadRecipient: Recipient, protected val re
|
||||||
|
|
||||||
fun getPrimaryText(context: Context): CharSequence {
|
fun getPrimaryText(context: Context): CharSequence {
|
||||||
return if (SignalStore.settings().messageNotificationsPrivacy.isDisplayMessage) {
|
return if (SignalStore.settings().messageNotificationsPrivacy.isDisplayMessage) {
|
||||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
if (RecipientUtil.isMessageRequestAccepted(context, thread.threadId)) {
|
||||||
getPrimaryTextActual(context)
|
getPrimaryTextActual(context)
|
||||||
} else {
|
} else {
|
||||||
SpanUtil.italic(context.getString(R.string.SingleRecipientNotificationBuilder_message_request))
|
SpanUtil.italic(context.getString(R.string.SingleRecipientNotificationBuilder_message_request))
|
||||||
|
@ -304,7 +304,7 @@ class ReactionNotification(threadRecipient: Recipient, record: MessageRecord, va
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStartingPosition(context: Context): Int {
|
override fun getStartingPosition(context: Context): Int {
|
||||||
return SignalDatabase.mmsSms.getMessagePositionInConversation(threadId, record.dateReceived)
|
return SignalDatabase.mmsSms.getMessagePositionInConversation(thread.threadId, record.dateReceived)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLargeIconUri(): Uri? = null
|
override fun getLargeIconUri(): Uri? = null
|
||||||
|
|
|
@ -21,7 +21,7 @@ object NotificationStateProvider {
|
||||||
private val TAG = Log.tag(NotificationStateProvider::class.java)
|
private val TAG = Log.tag(NotificationStateProvider::class.java)
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun constructNotificationState(stickyThreads: Map<Long, MessageNotifierV2.StickyThread>, notificationProfile: NotificationProfile?): NotificationStateV2 {
|
fun constructNotificationState(stickyThreads: Map<NotificationThread, MessageNotifierV2.StickyThread>, notificationProfile: NotificationProfile?): NotificationStateV2 {
|
||||||
val messages: MutableList<NotificationMessage> = mutableListOf()
|
val messages: MutableList<NotificationMessage> = mutableListOf()
|
||||||
|
|
||||||
SignalDatabase.mmsSms.getMessagesForNotificationState(stickyThreads.values).use { unreadMessages ->
|
SignalDatabase.mmsSms.getMessagesForNotificationState(stickyThreads.values).use { unreadMessages ->
|
||||||
|
@ -40,8 +40,8 @@ object NotificationStateProvider {
|
||||||
messageRecord = record,
|
messageRecord = record,
|
||||||
reactions = if (hasUnreadReactions) SignalDatabase.reactions.getReactions(MessageId(record.id, record.isMms)) else emptyList(),
|
reactions = if (hasUnreadReactions) SignalDatabase.reactions.getReactions(MessageId(record.id, record.isMms)) else emptyList(),
|
||||||
threadRecipient = threadRecipient,
|
threadRecipient = threadRecipient,
|
||||||
threadId = record.threadId,
|
thread = NotificationThread.fromMessageRecord(record),
|
||||||
stickyThread = stickyThreads.containsKey(record.threadId),
|
stickyThread = stickyThreads.containsKey(NotificationThread.fromMessageRecord(record)),
|
||||||
isUnreadMessage = CursorUtil.requireInt(unreadMessages, MmsSmsColumns.READ) == 0,
|
isUnreadMessage = CursorUtil.requireInt(unreadMessages, MmsSmsColumns.READ) == 0,
|
||||||
hasUnreadReactions = hasUnreadReactions,
|
hasUnreadReactions = hasUnreadReactions,
|
||||||
lastReactionRead = CursorUtil.requireLong(unreadMessages, MmsSmsColumns.REACTIONS_LAST_SEEN)
|
lastReactionRead = CursorUtil.requireLong(unreadMessages, MmsSmsColumns.REACTIONS_LAST_SEEN)
|
||||||
|
@ -62,8 +62,8 @@ object NotificationStateProvider {
|
||||||
val muteFilteredMessages: MutableList<NotificationStateV2.FilteredMessage> = mutableListOf()
|
val muteFilteredMessages: MutableList<NotificationStateV2.FilteredMessage> = mutableListOf()
|
||||||
val profileFilteredMessages: MutableList<NotificationStateV2.FilteredMessage> = mutableListOf()
|
val profileFilteredMessages: MutableList<NotificationStateV2.FilteredMessage> = mutableListOf()
|
||||||
|
|
||||||
messages.groupBy { it.threadId }
|
messages.groupBy { it.thread }
|
||||||
.forEach { (threadId, threadMessages) ->
|
.forEach { (thread, threadMessages) ->
|
||||||
var notificationItems: MutableList<NotificationItemV2> = mutableListOf()
|
var notificationItems: MutableList<NotificationItemV2> = mutableListOf()
|
||||||
|
|
||||||
for (notification: NotificationMessage in threadMessages) {
|
for (notification: NotificationMessage in threadMessages) {
|
||||||
|
@ -87,13 +87,13 @@ object NotificationStateProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationItems.sort()
|
notificationItems.sort()
|
||||||
if (notificationItems.isNotEmpty() && stickyThreads.containsKey(threadId) && !notificationItems.last().individualRecipient.isSelf) {
|
if (notificationItems.isNotEmpty() && stickyThreads.containsKey(thread) && !notificationItems.last().individualRecipient.isSelf) {
|
||||||
val indexOfOldestNonSelfMessage: Int = notificationItems.indexOfLast { it.individualRecipient.isSelf } + 1
|
val indexOfOldestNonSelfMessage: Int = notificationItems.indexOfLast { it.individualRecipient.isSelf } + 1
|
||||||
notificationItems = notificationItems.slice(indexOfOldestNonSelfMessage..notificationItems.lastIndex).toMutableList()
|
notificationItems = notificationItems.slice(indexOfOldestNonSelfMessage..notificationItems.lastIndex).toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notificationItems.isNotEmpty()) {
|
if (notificationItems.isNotEmpty()) {
|
||||||
conversations += NotificationConversation(notificationItems[0].threadRecipient, threadId, notificationItems)
|
conversations += NotificationConversation(notificationItems[0].threadRecipient, thread, notificationItems)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ object NotificationStateProvider {
|
||||||
val messageRecord: MessageRecord,
|
val messageRecord: MessageRecord,
|
||||||
val reactions: List<ReactionRecord>,
|
val reactions: List<ReactionRecord>,
|
||||||
val threadRecipient: Recipient,
|
val threadRecipient: Recipient,
|
||||||
val threadId: Long,
|
val thread: NotificationThread,
|
||||||
val stickyThread: Boolean,
|
val stickyThread: Boolean,
|
||||||
val isUnreadMessage: Boolean,
|
val isUnreadMessage: Boolean,
|
||||||
val hasUnreadReactions: Boolean,
|
val hasUnreadReactions: Boolean,
|
||||||
|
|
|
@ -39,21 +39,21 @@ data class NotificationStateV2(val conversations: List<NotificationConversation>
|
||||||
val mostRecentSender: Recipient?
|
val mostRecentSender: Recipient?
|
||||||
get() = mostRecentNotification?.individualRecipient
|
get() = mostRecentNotification?.individualRecipient
|
||||||
|
|
||||||
fun getNonVisibleConversation(visibleThreadId: Long): List<NotificationConversation> {
|
fun getNonVisibleConversation(visibleThread: NotificationThread?): List<NotificationConversation> {
|
||||||
return conversations.filterNot { it.threadId == visibleThreadId }
|
return conversations.filterNot { it.thread == visibleThread }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getConversation(threadId: Long): NotificationConversation? {
|
fun getConversation(notificationThread: NotificationThread): NotificationConversation? {
|
||||||
return conversations.firstOrNull { it.threadId == threadId }
|
return conversations.firstOrNull { it.thread == notificationThread }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDeleteIntent(context: Context): PendingIntent? {
|
fun getDeleteIntent(context: Context): PendingIntent? {
|
||||||
val ids = LongArray(messageCount)
|
val ids = LongArray(messageCount)
|
||||||
val mms = BooleanArray(ids.size)
|
val mms = BooleanArray(ids.size)
|
||||||
val threadIds: MutableList<Long> = mutableListOf()
|
val threads: MutableList<NotificationThread> = mutableListOf()
|
||||||
|
|
||||||
conversations.forEach { conversation ->
|
conversations.forEach { conversation ->
|
||||||
threadIds += conversation.threadId
|
threads += conversation.thread
|
||||||
conversation.notificationItems.forEachIndexed { index, notificationItem ->
|
conversation.notificationItems.forEachIndexed { index, notificationItem ->
|
||||||
ids[index] = notificationItem.id
|
ids[index] = notificationItem.id
|
||||||
mms[index] = notificationItem.isMms
|
mms[index] = notificationItem.isMms
|
||||||
|
@ -64,30 +64,24 @@ data class NotificationStateV2(val conversations: List<NotificationConversation>
|
||||||
.setAction(DeleteNotificationReceiver.DELETE_NOTIFICATION_ACTION)
|
.setAction(DeleteNotificationReceiver.DELETE_NOTIFICATION_ACTION)
|
||||||
.putExtra(DeleteNotificationReceiver.EXTRA_IDS, ids)
|
.putExtra(DeleteNotificationReceiver.EXTRA_IDS, ids)
|
||||||
.putExtra(DeleteNotificationReceiver.EXTRA_MMS, mms)
|
.putExtra(DeleteNotificationReceiver.EXTRA_MMS, mms)
|
||||||
.putExtra(DeleteNotificationReceiver.EXTRA_THREAD_IDS, threadIds.toLongArray())
|
.putParcelableArrayListExtra(DeleteNotificationReceiver.EXTRA_THREADS, ArrayList(threads))
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMarkAsReadIntent(context: Context): PendingIntent? {
|
fun getMarkAsReadIntent(context: Context): PendingIntent? {
|
||||||
val threadArray = LongArray(conversations.size)
|
|
||||||
|
|
||||||
conversations.forEachIndexed { index, conversation ->
|
|
||||||
threadArray[index] = conversation.threadId
|
|
||||||
}
|
|
||||||
|
|
||||||
val intent = Intent(context, MarkReadReceiver::class.java).setAction(MarkReadReceiver.CLEAR_ACTION)
|
val intent = Intent(context, MarkReadReceiver::class.java).setAction(MarkReadReceiver.CLEAR_ACTION)
|
||||||
.putExtra(MarkReadReceiver.THREAD_IDS_EXTRA, threadArray)
|
.putParcelableArrayListExtra(MarkReadReceiver.THREADS_EXTRA, ArrayList(conversations.map { it.thread }))
|
||||||
.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, NotificationIds.MESSAGE_SUMMARY)
|
.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, NotificationIds.MESSAGE_SUMMARY)
|
||||||
.makeUniqueToPreventMerging()
|
.makeUniqueToPreventMerging()
|
||||||
|
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getThreadsWithMostRecentNotificationFromSelf(): Set<Long> {
|
fun getThreadsWithMostRecentNotificationFromSelf(): Set<NotificationThread> {
|
||||||
return conversations.filter { it.mostRecentNotification.individualRecipient.isSelf }
|
return conversations.filter { it.mostRecentNotification.individualRecipient.isSelf }
|
||||||
.map { it.threadId }
|
.map { it.thread }
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.thoughtcrime.securesms.notifications.v2
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
|
import org.thoughtcrime.securesms.database.model.ParentStoryId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a "thread" that a notification can belong to.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class NotificationThread(
|
||||||
|
val threadId: Long,
|
||||||
|
val groupStoryId: Long?
|
||||||
|
) : Parcelable {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun forConversation(threadId: Long): NotificationThread {
|
||||||
|
return NotificationThread(
|
||||||
|
threadId = threadId,
|
||||||
|
groupStoryId = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun fromMessageRecord(record: MessageRecord): NotificationThread {
|
||||||
|
return NotificationThread(record.threadId, ((record as? MmsMessageRecord)?.parentStoryId as? ParentStoryId.GroupReply)?.serialize())
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun fromThreadAndReply(threadId: Long, groupReply: ParentStoryId.GroupReply?): NotificationThread {
|
||||||
|
return NotificationThread(threadId, groupReply?.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.messages.GroupSendUtil;
|
import org.thoughtcrime.securesms.messages.GroupSendUtil;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
@ -352,7 +353,7 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
|
||||||
peekInfo.getJoinedMembers(),
|
peekInfo.getJoinedMembers(),
|
||||||
WebRtcUtil.isCallFull(peekInfo));
|
WebRtcUtil.isCallFull(peekInfo));
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId, true, 0, BubbleUtil.BubbleState.HIDDEN);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(threadId), true, 0, BubbleUtil.BubbleState.HIDDEN);
|
||||||
|
|
||||||
EventBus.getDefault().postSticky(new GroupCallPeekEvent(id, peekInfo.getEraId(), peekInfo.getDeviceCount(), peekInfo.getMaxDevices()));
|
EventBus.getDefault().postSticky(new GroupCallPeekEvent(id, peekInfo.getEraId(), peekInfo.getDeviceCount(), peekInfo.getMaxDevices()));
|
||||||
}
|
}
|
||||||
|
@ -835,14 +836,14 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
|
||||||
Pair<Long, Long> messageAndThreadId = SignalDatabase.sms().insertMissedCall(remotePeer.getId(), timestamp, isVideoOffer);
|
Pair<Long, Long> messageAndThreadId = SignalDatabase.sms().insertMissedCall(remotePeer.getId(), timestamp, isVideoOffer);
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier()
|
ApplicationDependencies.getMessageNotifier()
|
||||||
.updateNotification(context, messageAndThreadId.second(), signal);
|
.updateNotification(context, NotificationThread.forConversation(messageAndThreadId.second()), signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertReceivedCall(@NonNull RemotePeer remotePeer, boolean signal, boolean isVideoOffer) {
|
public void insertReceivedCall(@NonNull RemotePeer remotePeer, boolean signal, boolean isVideoOffer) {
|
||||||
Pair<Long, Long> messageAndThreadId = SignalDatabase.sms().insertReceivedCall(remotePeer.getId(), isVideoOffer);
|
Pair<Long, Long> messageAndThreadId = SignalDatabase.sms().insertReceivedCall(remotePeer.getId(), isVideoOffer);
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier()
|
ApplicationDependencies.getMessageNotifier()
|
||||||
.updateNotification(context, messageAndThreadId.second(), signal);
|
.updateNotification(context, NotificationThread.forConversation(messageAndThreadId.second()), signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void retrieveTurnServers(@NonNull RemotePeer remotePeer) {
|
public void retrieveTurnServers(@NonNull RemotePeer remotePeer) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||||
import org.thoughtcrime.securesms.notifications.v2.NotificationFactory;
|
import org.thoughtcrime.securesms.notifications.v2.NotificationFactory;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
|
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
@ -75,11 +76,12 @@ public final class BubbleUtil {
|
||||||
*/
|
*/
|
||||||
public static void displayAsBubble(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) {
|
public static void displayAsBubble(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) {
|
||||||
if (Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION) {
|
if (Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION) {
|
||||||
|
NotificationThread notificationThread = NotificationThread.forConversation(threadId);
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
if (canBubble(context, recipientId, threadId)) {
|
if (canBubble(context, recipientId, threadId)) {
|
||||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||||
StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
|
StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
|
||||||
int threadNotificationId = NotificationIds.getNotificationIdForThread(threadId);
|
int threadNotificationId = NotificationIds.getNotificationIdForThread(notificationThread);
|
||||||
Notification activeThreadNotification = Stream.of(notifications)
|
Notification activeThreadNotification = Stream.of(notifications)
|
||||||
.filter(n -> n.getId() == threadNotificationId)
|
.filter(n -> n.getId() == threadNotificationId)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
@ -87,7 +89,7 @@ public final class BubbleUtil {
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|
||||||
if (activeThreadNotification != null && activeThreadNotification.deleteIntent != null) {
|
if (activeThreadNotification != null && activeThreadNotification.deleteIntent != null) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId, BubbleState.SHOWN);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, notificationThread, BubbleState.SHOWN);
|
||||||
} else {
|
} else {
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
NotificationFactory.notifyToBubbleConversation(context, recipient, threadId);
|
NotificationFactory.notifyToBubbleConversation(context, recipient, threadId);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.database.MessageDatabase.InsertResult;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.IdentityRecord;
|
import org.thoughtcrime.securesms.database.model.IdentityRecord;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.notifications.v2.NotificationThread;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingIdentityDefaultMessage;
|
import org.thoughtcrime.securesms.sms.IncomingIdentityDefaultMessage;
|
||||||
|
@ -140,7 +141,7 @@ public final class IdentityUtil {
|
||||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
|
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
ApplicationDependencies.getMessageNotifier().updateNotification(context, NotificationThread.forConversation(insertResult.get().getThreadId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.signal.core.util
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for lower level API compatibility with the new Pending Intents flags.
|
||||||
|
*
|
||||||
|
* This is meant to be a replacement to using PendingIntent flags independently, and should
|
||||||
|
* end up being the only place in our codebase that accesses these values.
|
||||||
|
*
|
||||||
|
* The "default" value is FLAG_MUTABLE
|
||||||
|
*/
|
||||||
|
object PendingIntentFlags {
|
||||||
|
|
||||||
|
fun updateCurrent(): Int {
|
||||||
|
return mutable() or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelCurrent(): Int {
|
||||||
|
return mutable() or PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backwards compatible "default" value for pending intent flags.
|
||||||
|
*/
|
||||||
|
fun mutable(): Int {
|
||||||
|
return if (Build.VERSION.SDK_INT >= 31) PendingIntent.FLAG_MUTABLE else 0
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue