Add notification for failed story messages.
This commit is contained in:
parent
069b707d9d
commit
150c42c590
14 changed files with 135 additions and 3 deletions
|
@ -10,4 +10,5 @@ public final class EmojiStrings {
|
|||
public static final String STICKER = "\u2B50";
|
||||
public static final String GIFT = "\uD83C\uDF81";
|
||||
public static final String CARD = "\uD83D\uDCB3";
|
||||
public static final String FAILED_STORY = "\u2757";
|
||||
}
|
||||
|
|
|
@ -1314,6 +1314,17 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
|||
return markedMessageInfos
|
||||
}
|
||||
|
||||
fun markAllFailedStoriesNotified() {
|
||||
val where = "$IS_STORY_CLAUSE AND (${getOutgoingTypeClause()}) AND $NOTIFIED = 0 AND ($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_FAILED_TYPE}"
|
||||
|
||||
writableDatabase
|
||||
.update("$TABLE_NAME INDEXED BY $INDEX_THREAD_DATE")
|
||||
.values(NOTIFIED to 1)
|
||||
.where(where)
|
||||
.run()
|
||||
notifyConversationListListeners()
|
||||
}
|
||||
|
||||
fun markOnboardingStoryRead() {
|
||||
val recipientId = SignalStore.releaseChannelValues().releaseChannelRecipientId ?: return
|
||||
val where = "$IS_STORY_CLAUSE AND NOT (${getOutgoingTypeClause()}) AND $READ = 0 AND $RECIPIENT_ID = ?"
|
||||
|
@ -1459,6 +1470,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
|||
.readToList { RecipientId.from(it.getLong(0)) }
|
||||
}
|
||||
|
||||
fun hasFailedOutgoingStory(): Boolean {
|
||||
val where = "$IS_STORY_CLAUSE AND (${getOutgoingTypeClause()}) AND $NOTIFIED = 0 AND ($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_FAILED_TYPE}"
|
||||
return readableDatabase.exists(TABLE_NAME).where(where).run()
|
||||
}
|
||||
|
||||
fun getOrderedStoryRecipientsAndIds(isOutgoingOnly: Boolean): List<StoryResult> {
|
||||
val query = """
|
||||
SELECT
|
||||
|
@ -2002,6 +2018,14 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
|||
.run()
|
||||
}
|
||||
|
||||
fun markAsNotNotified(id: Long) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(NOTIFIED to 0)
|
||||
.where("$ID = ?", id)
|
||||
.run()
|
||||
}
|
||||
|
||||
fun setMessagesReadSince(threadId: Long, sinceTimestamp: Long): List<MarkedMessageInfo> {
|
||||
var query = """
|
||||
$THREAD_ID = ? AND
|
||||
|
|
|
@ -26,8 +26,11 @@ import org.thoughtcrime.securesms.blurhash.BlurHash;
|
|||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ParentStoryId;
|
||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||
|
@ -41,6 +44,7 @@ import org.thoughtcrime.securesms.keyvalue.CertificateType;
|
|||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel;
|
||||
|
@ -287,10 +291,25 @@ public abstract class PushSendJob extends SendJob {
|
|||
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
|
||||
ParentStoryId.GroupReply groupReplyStoryId = SignalDatabase.messages().getParentStoryIdForGroupReply(messageId);
|
||||
|
||||
boolean isStory = false;
|
||||
try {
|
||||
MessageRecord record = SignalDatabase.messages().getMessageRecord(messageId);
|
||||
if (record instanceof MmsMessageRecord) {
|
||||
isStory = (((MmsMessageRecord) record).getStoryType().isStory());
|
||||
}
|
||||
} catch (NoSuchMessageException e) {
|
||||
Log.e(TAG, e);
|
||||
}
|
||||
|
||||
if (threadId != -1 && recipient != null) {
|
||||
if (isStory) {
|
||||
SignalDatabase.messages().markAsNotNotified(messageId);
|
||||
ApplicationDependencies.getMessageNotifier().notifyStoryDeliveryFailed(context, recipient, ConversationId.forConversation(threadId));
|
||||
} else {
|
||||
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, recipient, ConversationId.fromThreadAndReply(threadId, groupReplyStoryId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Optional<SignalServiceDataMessage.Quote> getQuoteFor(OutgoingMessage message) throws IOException {
|
||||
if (message.getOutgoingQuote() == null) return Optional.empty();
|
||||
|
|
|
@ -21,6 +21,7 @@ public interface MessageNotifier {
|
|||
void clearVisibleThread();
|
||||
void setLastDesktopActivityTimestamp(long timestamp);
|
||||
void notifyMessageDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId conversationId);
|
||||
void notifyStoryDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId conversationId);
|
||||
void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId conversationId);
|
||||
void cancelDelayedNotifications();
|
||||
void updateNotification(@NonNull Context context);
|
||||
|
|
|
@ -58,6 +58,11 @@ public class OptimizedMessageNotifier implements MessageNotifier {
|
|||
getNotifier().notifyMessageDeliveryFailed(context, recipient, conversationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyStoryDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId threadId) {
|
||||
getNotifier().notifyStoryDeliveryFailed(context, recipient, threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId conversationId) {
|
||||
getNotifier().notifyProofRequired(context, recipient, conversationId);
|
||||
|
|
|
@ -85,6 +85,10 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier {
|
|||
NotificationFactory.notifyMessageDeliveryFailed(context, recipient, conversationId, visibleThread)
|
||||
}
|
||||
|
||||
override fun notifyStoryDeliveryFailed(context: Context, recipient: Recipient, conversationId: ConversationId) {
|
||||
NotificationFactory.notifyStoryDeliveryFailed(context, recipient, conversationId)
|
||||
}
|
||||
|
||||
override fun notifyProofRequired(context: Context, recipient: Recipient, conversationId: ConversationId) {
|
||||
NotificationFactory.notifyProofRequired(context, recipient, conversationId, visibleThread)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@ import org.signal.core.util.concurrent.SignalExecutors
|
|||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.MainActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiStrings
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
@ -324,6 +327,46 @@ object NotificationFactory {
|
|||
NotificationManagerCompat.from(context).safelyNotify(recipient, NotificationIds.getNotificationIdForMessageDeliveryFailed(thread), builder.build())
|
||||
}
|
||||
|
||||
fun notifyStoryDeliveryFailed(context: Context, recipient: Recipient, thread: ConversationId) {
|
||||
val intent = Intent(context, MyStoriesActivity::class.java).makeUniqueToPreventMerging()
|
||||
|
||||
val contentTitle = if (SignalStore.settings().messageNotificationsPrivacy.isDisplayContact) {
|
||||
if (recipient.isGroup) {
|
||||
context.getString(R.string.MessageNotifier_group_story_title, recipient.getDisplayName(context))
|
||||
} else {
|
||||
recipient.getDisplayName(context)
|
||||
}
|
||||
} else {
|
||||
context.getString(R.string.SingleRecipientNotificationBuilder_signal)
|
||||
}
|
||||
|
||||
val largeIcon = if (SignalStore.settings().messageNotificationsPrivacy.isDisplayContact) {
|
||||
if (recipient.isMyStory) {
|
||||
Recipient.self().getContactDrawable(context)
|
||||
} else {
|
||||
recipient.getContactDrawable(context)
|
||||
}
|
||||
} else {
|
||||
GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, AvatarColor.UNKNOWN)
|
||||
}.toLargeBitmap(context)
|
||||
|
||||
val builder: NotificationBuilder = NotificationBuilder.create(context)
|
||||
|
||||
builder.apply {
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setLargeIcon(largeIcon)
|
||||
setContentTitle(contentTitle)
|
||||
setContentText(String.format("%s %s", EmojiStrings.FAILED_STORY, context.getString(R.string.MessageNotifier_story_delivery_failed)))
|
||||
setTicker(context.getString(R.string.MessageNotifier_story_delivery_failed))
|
||||
setContentIntent(NotificationPendingIntentHelper.getActivity(context, 0, intent, PendingIntentFlags.mutable()))
|
||||
setAutoCancel(true)
|
||||
setAlarms(recipient)
|
||||
setChannelId(NotificationChannels.getInstance().FAILURES)
|
||||
}
|
||||
|
||||
NotificationManagerCompat.from(context).safelyNotify(recipient, NotificationIds.getNotificationIdForMessageDeliveryFailed(thread), builder.build())
|
||||
}
|
||||
|
||||
fun notifyProofRequired(context: Context, recipient: Recipient, thread: ConversationId, visibleThread: ConversationId?) {
|
||||
if (thread == visibleThread) {
|
||||
notifyInThread(context, recipient, 0)
|
||||
|
|
|
@ -185,4 +185,13 @@ class StoriesLandingRepository(context: Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all failed stories as "notified" by the user (marking them as notified in the database)
|
||||
*/
|
||||
fun markFailedStoriesNotified() {
|
||||
SignalExecutors.BOUNDED_IO.execute {
|
||||
SignalDatabase.messages.markAllFailedStoriesNotified()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ class StoriesLandingViewModel(private val storiesLandingRepository: StoriesLandi
|
|||
|
||||
fun markStoriesRead() {
|
||||
storiesLandingRepository.markStoriesRead()
|
||||
storiesLandingRepository.markFailedStoriesNotified()
|
||||
}
|
||||
|
||||
class Factory(private val storiesLandingRepository: StoriesLandingRepository) : ViewModelProvider.Factory {
|
||||
|
|
|
@ -47,6 +47,22 @@ class ConversationListTabRepository {
|
|||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
fun getHasFailedOutgoingStories(): Observable<Boolean> {
|
||||
return Observable.create<Boolean> { emitter ->
|
||||
fun refresh() {
|
||||
emitter.onNext(SignalDatabase.messages.hasFailedOutgoingStory())
|
||||
}
|
||||
|
||||
val listener = DatabaseObserver.Observer {
|
||||
refresh()
|
||||
}
|
||||
|
||||
ApplicationDependencies.getDatabaseObserver().registerConversationListObserver(listener)
|
||||
emitter.setCancellable { ApplicationDependencies.getDatabaseObserver().unregisterObserver(listener) }
|
||||
refresh()
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
fun getNumberOfUnseenCalls(): Observable<Long> {
|
||||
return Observable.create { emitter ->
|
||||
fun refresh() {
|
||||
|
|
|
@ -120,8 +120,8 @@ class ConversationListTabsFragment : Fragment(R.layout.conversation_list_tabs) {
|
|||
binding.chatsUnreadIndicator.visible = state.unreadMessagesCount > 0
|
||||
binding.chatsUnreadIndicator.text = formatCount(state.unreadMessagesCount)
|
||||
|
||||
binding.storiesUnreadIndicator.visible = state.unreadStoriesCount > 0
|
||||
binding.storiesUnreadIndicator.text = formatCount(state.unreadStoriesCount)
|
||||
binding.storiesUnreadIndicator.visible = state.unreadStoriesCount > 0 || state.hasFailedStory
|
||||
binding.storiesUnreadIndicator.text = if (state.hasFailedStory) "!" else formatCount(state.unreadStoriesCount)
|
||||
|
||||
if (FeatureFlags.callsTab()) {
|
||||
binding.callsUnreadIndicator.visible = state.unreadCallsCount > 0
|
||||
|
|
|
@ -6,6 +6,7 @@ data class ConversationListTabsState(
|
|||
val unreadMessagesCount: Long = 0L,
|
||||
val unreadCallsCount: Long = 0L,
|
||||
val unreadStoriesCount: Long = 0L,
|
||||
val hasFailedStory: Boolean = false,
|
||||
val visibilityState: VisibilityState = VisibilityState()
|
||||
) {
|
||||
data class VisibilityState(
|
||||
|
|
|
@ -34,6 +34,10 @@ class ConversationListTabsViewModel(repository: ConversationListTabRepository) :
|
|||
disposables += repository.getNumberOfUnseenStories().subscribe { unseenStories ->
|
||||
store.update { it.copy(unreadStoriesCount = unseenStories) }
|
||||
}
|
||||
|
||||
disposables += repository.getHasFailedOutgoingStories().subscribe { hasFailedStories ->
|
||||
store.update { it.copy(hasFailedStory = hasFailedStories) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
|
|
@ -2129,6 +2129,10 @@
|
|||
<string name="MessageNotifier_most_recent_from_s">Most recent from: %1$s</string>
|
||||
<string name="MessageNotifier_locked_message">Locked message</string>
|
||||
<string name="MessageNotifier_message_delivery_failed">Message delivery failed.</string>
|
||||
<!-- Shown in a notification when a story the user tries to send fails to be sent -->
|
||||
<string name="MessageNotifier_story_delivery_failed">Story failed to send</string>
|
||||
<!-- Shown as notification title for when a notification about a story sent to a group story %1$s replaced with the group name -->
|
||||
<string name="MessageNotifier_group_story_title">You to %1$s</string>
|
||||
<string name="MessageNotifier_failed_to_deliver_message">Failed to deliver message.</string>
|
||||
<string name="MessageNotifier_error_delivering_message">Error delivering message.</string>
|
||||
<string name="MessageNotifier_message_delivery_paused">Message delivery paused.</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue