Apply MessageStyle and fix chronology issues.
parent
fe5fca8eaf
commit
3bd8aa8a86
|
@ -37,7 +37,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
|
@ -71,11 +70,9 @@ import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder;
|
|||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -170,6 +167,30 @@ public class MessageNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean isDisplayingSummaryNotification(@NonNull Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
try {
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();
|
||||
|
||||
for (StatusBarNotification activeNotification : activeNotifications) {
|
||||
if (activeNotification.getId() == SUMMARY_NOTIFICATION_ID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} catch (Throwable e) {
|
||||
// XXX Android ROM Bug, see #6043
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void cancelOrphanedNotifications(@NonNull Context context, NotificationState notificationState) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
try {
|
||||
|
@ -209,7 +230,7 @@ public class MessageNotifier {
|
|||
return;
|
||||
}
|
||||
|
||||
updateNotification(context, false, 0);
|
||||
updateNotification(context, -1, false, 0);
|
||||
}
|
||||
|
||||
public static void updateNotification(@NonNull Context context, long threadId)
|
||||
|
@ -226,7 +247,7 @@ public class MessageNotifier {
|
|||
long threadId,
|
||||
boolean signal)
|
||||
{
|
||||
boolean isVisible = visibleThread == threadId;
|
||||
boolean isVisible = visibleThread == threadId;
|
||||
|
||||
ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context);
|
||||
Recipient recipients = DatabaseFactory.getThreadDatabase(context)
|
||||
|
@ -246,11 +267,12 @@ public class MessageNotifier {
|
|||
if (isVisible) {
|
||||
sendInThreadNotification(context, threads.getRecipientForThreadId(threadId));
|
||||
} else {
|
||||
updateNotification(context, signal, 0);
|
||||
updateNotification(context, threadId, signal, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateNotification(@NonNull Context context,
|
||||
long targetThread,
|
||||
boolean signal,
|
||||
int reminderCount)
|
||||
{
|
||||
|
@ -281,13 +303,22 @@ public class MessageNotifier {
|
|||
if (notificationState.hasMultipleThreads()) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
for (long threadId : notificationState.getThreads()) {
|
||||
sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true);
|
||||
if (targetThread < 1 || targetThread == threadId) {
|
||||
sendSingleThreadNotification(context,
|
||||
new NotificationState(notificationState.getNotificationsForThread(threadId)),
|
||||
signal && (threadId == targetThread),
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMultipleThreadNotification(context, notificationState, signal);
|
||||
sendMultipleThreadNotification(context, notificationState, signal && (Build.VERSION.SDK_INT < 23));
|
||||
} else {
|
||||
sendSingleThreadNotification(context, notificationState, signal, false);
|
||||
|
||||
if (isDisplayingSummaryNotification(context)) {
|
||||
sendMultipleThreadNotification(context, notificationState, false);
|
||||
}
|
||||
}
|
||||
|
||||
cancelOrphanedNotifications(context, notificationState);
|
||||
|
@ -302,9 +333,10 @@ public class MessageNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
private static void sendSingleThreadNotification(@NonNull Context context,
|
||||
@NonNull NotificationState notificationState,
|
||||
boolean signal, boolean bundled)
|
||||
private static void sendSingleThreadNotification(@NonNull Context context,
|
||||
@NonNull NotificationState notificationState,
|
||||
boolean signal,
|
||||
boolean bundled)
|
||||
{
|
||||
Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled);
|
||||
|
||||
|
@ -317,8 +349,13 @@ public class MessageNotifier {
|
|||
SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context));
|
||||
List<NotificationItem> notifications = notificationState.getNotifications();
|
||||
Recipient recipient = notifications.get(0).getRecipient();
|
||||
int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0));
|
||||
int notificationId;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
notificationId = (int) (SUMMARY_NOTIFICATION_ID + notifications.get(0).getThreadId());
|
||||
} else {
|
||||
notificationId = SUMMARY_NOTIFICATION_ID;
|
||||
}
|
||||
|
||||
builder.setThread(notifications.get(0).getRecipient());
|
||||
builder.setMessageCount(notificationState.getMessageCount());
|
||||
|
@ -327,7 +364,7 @@ public class MessageNotifier {
|
|||
builder.setContentIntent(notifications.get(0).getPendingIntent(context));
|
||||
builder.setDeleteIntent(notificationState.getDeleteIntent(context));
|
||||
builder.setOnlyAlertOnce(!signal);
|
||||
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
|
||||
builder.setSortKey(String.valueOf(Long.MAX_VALUE - notifications.get(0).getTimestamp()));
|
||||
|
||||
long timestamp = notifications.get(0).getTimestamp();
|
||||
if (timestamp != 0) builder.setWhen(timestamp);
|
||||
|
@ -348,7 +385,7 @@ public class MessageNotifier {
|
|||
|
||||
while(iterator.hasPrevious()) {
|
||||
NotificationItem item = iterator.previous();
|
||||
builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText());
|
||||
builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText(), item.getTimestamp());
|
||||
}
|
||||
|
||||
if (signal) {
|
||||
|
@ -357,9 +394,9 @@ public class MessageNotifier {
|
|||
notifications.get(0).getText());
|
||||
}
|
||||
|
||||
if (bundled) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
builder.setGroup(NOTIFICATION_GROUP);
|
||||
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
|
||||
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
|
||||
}
|
||||
|
||||
Notification notification = builder.build();
|
||||
|
@ -378,10 +415,13 @@ public class MessageNotifier {
|
|||
|
||||
builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount());
|
||||
builder.setMostRecentSender(notifications.get(0).getIndividualRecipient());
|
||||
builder.setGroup(NOTIFICATION_GROUP);
|
||||
builder.setDeleteIntent(notificationState.getDeleteIntent(context));
|
||||
builder.setOnlyAlertOnce(!signal);
|
||||
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
builder.setGroup(NOTIFICATION_GROUP);
|
||||
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
|
||||
}
|
||||
|
||||
long timestamp = notifications.get(0).getTimestamp();
|
||||
if (timestamp != 0) builder.setWhen(timestamp);
|
||||
|
@ -599,7 +639,7 @@ public class MessageNotifier {
|
|||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
int reminderCount = intent.getIntExtra("reminder_count", 0);
|
||||
MessageNotifier.updateNotification(context, true, reminderCount + 1);
|
||||
MessageNotifier.updateNotification(context, -1, true, reminderCount + 1);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,13 @@ import androidx.annotation.StringRes;
|
|||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationCompat.Action;
|
||||
import androidx.core.app.Person;
|
||||
import androidx.core.app.RemoteInput;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -46,11 +50,12 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
private static final int BIG_PICTURE_DIMEN = 500;
|
||||
private static final int LARGE_ICON_DIMEN = 250;
|
||||
|
||||
private final List<CharSequence> messageBodies = new LinkedList<>();
|
||||
private final List<NotificationCompat.MessagingStyle.Message> messages = new LinkedList<>();
|
||||
|
||||
private SlideDeck slideDeck;
|
||||
private CharSequence contentTitle;
|
||||
private CharSequence contentText;
|
||||
private Recipient threadRecipient;
|
||||
|
||||
public SingleRecipientNotificationBuilder(@NonNull Context context, @NonNull NotificationPrivacyPreference privacy)
|
||||
{
|
||||
|
@ -76,25 +81,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
addPerson(recipient.getContactUri().toString());
|
||||
}
|
||||
|
||||
ContactPhoto contactPhoto = recipient.getContactPhoto();
|
||||
FallbackContactPhoto fallbackContactPhoto = recipient.getFallbackContactPhoto();
|
||||
|
||||
if (contactPhoto != null) {
|
||||
try {
|
||||
setLargeIcon(GlideApp.with(context.getApplicationContext())
|
||||
.load(contactPhoto)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.circleCrop()
|
||||
.submit(context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
|
||||
context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height))
|
||||
.get());
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
Log.w(TAG, e);
|
||||
setLargeIcon(fallbackContactPhoto.asDrawable(context, recipient.getColor().toConversationColor(context)));
|
||||
}
|
||||
} else {
|
||||
setLargeIcon(fallbackContactPhoto.asDrawable(context, recipient.getColor().toConversationColor(context)));
|
||||
}
|
||||
setLargeIcon(getContactDrawable(recipient));
|
||||
|
||||
} else {
|
||||
setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal));
|
||||
|
@ -102,6 +89,27 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
}
|
||||
}
|
||||
|
||||
private Drawable getContactDrawable(@NonNull Recipient recipient) {
|
||||
ContactPhoto contactPhoto = recipient.getContactPhoto();
|
||||
FallbackContactPhoto fallbackContactPhoto = recipient.getFallbackContactPhoto();
|
||||
|
||||
if (contactPhoto != null) {
|
||||
try {
|
||||
return GlideApp.with(context.getApplicationContext())
|
||||
.load(contactPhoto)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.circleCrop()
|
||||
.submit(context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
|
||||
context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height))
|
||||
.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
return fallbackContactPhoto.asDrawable(context, recipient.getColor().toConversationColor(context));
|
||||
}
|
||||
} else {
|
||||
return fallbackContactPhoto.asDrawable(context, recipient.getColor().toConversationColor(context));
|
||||
}
|
||||
}
|
||||
|
||||
public void setMessageCount(int messageCount) {
|
||||
setContentInfo(String.valueOf(messageCount));
|
||||
setNumber(messageCount);
|
||||
|
@ -139,10 +147,10 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
|
||||
NotificationCompat.CarExtender.UnreadConversation.Builder unreadConversationBuilder =
|
||||
new NotificationCompat.CarExtender.UnreadConversation.Builder(contentTitle.toString())
|
||||
.addMessage(contentText.toString())
|
||||
.setLatestTimestamp(timestamp)
|
||||
.setReadPendingIntent(androidAutoHeardIntent)
|
||||
.setReplyAction(androidAutoReplyIntent, remoteInput);
|
||||
.addMessage(contentText.toString())
|
||||
.setLatestTimestamp(timestamp)
|
||||
.setReadPendingIntent(androidAutoHeardIntent)
|
||||
.setReplyAction(androidAutoReplyIntent, remoteInput);
|
||||
|
||||
extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConversationBuilder.build()));
|
||||
}
|
||||
|
@ -202,19 +210,35 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
|
||||
public void addMessageBody(@NonNull Recipient threadRecipient,
|
||||
@NonNull Recipient individualRecipient,
|
||||
@Nullable CharSequence messageBody)
|
||||
@Nullable CharSequence messageBody,
|
||||
long timestamp)
|
||||
{
|
||||
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
|
||||
Person.Builder personBuilder = new Person.Builder()
|
||||
.setKey(individualRecipient.getId().serialize())
|
||||
.setBot(false);
|
||||
|
||||
if (privacy.isDisplayContact() && threadRecipient.isGroup()) {
|
||||
stringBuilder.append(Util.getBoldedString(individualRecipient.toShortString(context) + ": "));
|
||||
}
|
||||
this.threadRecipient = threadRecipient;
|
||||
|
||||
if (privacy.isDisplayMessage()) {
|
||||
messageBodies.add(stringBuilder.append(messageBody == null ? "" : messageBody));
|
||||
if (privacy.isDisplayContact()) {
|
||||
personBuilder.setName(individualRecipient.getDisplayName(context));
|
||||
|
||||
Bitmap bitmap = getLargeBitmap(getContactDrawable(individualRecipient));
|
||||
if (bitmap != null) {
|
||||
personBuilder.setIcon(IconCompat.createWithBitmap(bitmap));
|
||||
}
|
||||
} else {
|
||||
messageBodies.add(stringBuilder.append(context.getString(R.string.SingleRecipientNotificationBuilder_new_message)));
|
||||
personBuilder.setName("");
|
||||
}
|
||||
|
||||
final CharSequence text;
|
||||
if (privacy.isDisplayMessage()) {
|
||||
text = messageBody == null ? "" : messageBody;
|
||||
} else {
|
||||
text = stringBuilder.append(context.getString(R.string.SingleRecipientNotificationBuilder_new_message));
|
||||
}
|
||||
|
||||
messages.add(new NotificationCompat.MessagingStyle.Message(text, timestamp, personBuilder.build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -223,33 +247,68 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
Optional<Uri> largeIconUri = getLargeIconUri(slideDeck);
|
||||
Optional<Uri> bigPictureUri = getBigPictureUri(slideDeck);
|
||||
|
||||
if (messageBodies.size() == 1 && largeIconUri.isPresent()) {
|
||||
if (messages.size() == 1 && largeIconUri.isPresent()) {
|
||||
setLargeIcon(getNotificationPicture(largeIconUri.get(), LARGE_ICON_DIMEN));
|
||||
}
|
||||
|
||||
if (messageBodies.size() == 1 && bigPictureUri.isPresent()) {
|
||||
if (messages.size() == 1 && bigPictureUri.isPresent()) {
|
||||
setStyle(new NotificationCompat.BigPictureStyle()
|
||||
.bigPicture(getNotificationPicture(bigPictureUri.get(), BIG_PICTURE_DIMEN))
|
||||
.setSummaryText(getBigText(messageBodies)));
|
||||
.setSummaryText(getBigText()));
|
||||
} else {
|
||||
setStyle(new NotificationCompat.BigTextStyle().bigText(getBigText(messageBodies)));
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
applyMessageStyle();
|
||||
} else {
|
||||
applyLegacy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.build();
|
||||
}
|
||||
|
||||
private void applyMessageStyle() {
|
||||
NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(
|
||||
new Person.Builder()
|
||||
.setBot(false)
|
||||
.setName(Recipient.self().getDisplayName(context))
|
||||
.setKey(Recipient.self().getId().serialize())
|
||||
.build());
|
||||
|
||||
if (threadRecipient.isGroup()) {
|
||||
if (privacy.isDisplayContact()) {
|
||||
messagingStyle.setConversationTitle(threadRecipient.getDisplayName(context));
|
||||
} else {
|
||||
messagingStyle.setConversationTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal));
|
||||
}
|
||||
|
||||
messagingStyle.setGroupConversation(true);
|
||||
}
|
||||
|
||||
Stream.of(messages).forEach(messagingStyle::addMessage);
|
||||
setStyle(messagingStyle);
|
||||
}
|
||||
|
||||
private void applyLegacy() {
|
||||
setStyle(new NotificationCompat.BigTextStyle().bigText(getBigText()));
|
||||
}
|
||||
|
||||
private void setLargeIcon(@Nullable Drawable drawable) {
|
||||
if (drawable != null) {
|
||||
int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
|
||||
Bitmap recipientPhotoBitmap = BitmapUtil.createFromDrawable(drawable, largeIconTargetSize, largeIconTargetSize);
|
||||
|
||||
if (recipientPhotoBitmap != null) {
|
||||
setLargeIcon(recipientPhotoBitmap);
|
||||
}
|
||||
setLargeIcon(getLargeBitmap(drawable));
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Bitmap getLargeBitmap(@Nullable Drawable drawable) {
|
||||
if (drawable != null) {
|
||||
int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
|
||||
|
||||
return BitmapUtil.createFromDrawable(drawable, largeIconTargetSize, largeIconTargetSize);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Optional<Uri> getLargeIconUri(@Nullable SlideDeck slideDeck) {
|
||||
if (slideDeck == null) {
|
||||
return Optional.absent();
|
||||
|
@ -302,12 +361,12 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
return super.setContentText(this.contentText);
|
||||
}
|
||||
|
||||
private CharSequence getBigText(List<CharSequence> messageBodies) {
|
||||
private CharSequence getBigText() {
|
||||
SpannableStringBuilder content = new SpannableStringBuilder();
|
||||
|
||||
for (int i = 0; i < messageBodies.size(); i++) {
|
||||
content.append(trimToDisplayLength(messageBodies.get(i)));
|
||||
if (i < messageBodies.size() - 1) {
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
content.append(getBigTextFor(messages.get(i)));
|
||||
if (i < messages.size() - 1) {
|
||||
content.append('\n');
|
||||
}
|
||||
}
|
||||
|
@ -315,4 +374,14 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||
return content;
|
||||
}
|
||||
|
||||
private CharSequence getBigTextFor(NotificationCompat.MessagingStyle.Message message) {
|
||||
SpannableStringBuilder content = new SpannableStringBuilder();
|
||||
|
||||
if (message.getPerson() != null && message.getPerson().getName() != null && threadRecipient.isGroup()) {
|
||||
content.append(Util.getBoldedString(message.getPerson().getName().toString())).append(": ");
|
||||
}
|
||||
|
||||
return trimToDisplayLength(content.append(message.getText()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue