diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 395885cfc6..8d133a0481 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -180,24 +180,15 @@ 2 - - #ffF44336 - #ffE91E63 - #ff9C27B0 - #ff673AB7 - #ff3F51B5 - #ff2196F3 - #ff03A9F4 - #ff00BCD4 - #ff009688 - #ff4CAF50 - #ff8BC34A - - #FFFFC107 - #ffFF9800 - #ffFF5722 - #ff795548 - #ff607D8B - + + @string/arrays__name_and_message + @string/arrays__name_only + @string/arrays__neither + + + all + contact + none + diff --git a/res/values/strings.xml b/res/values/strings.xml index 0c6dac0a86..f92883ecfa 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -467,7 +467,7 @@ Lock with passphrase - %1$d messages in %2$d conversations + %1$d new messages in %2$d conversations Most recent from: %1$s Locked message... Media message: %s @@ -484,6 +484,10 @@ Quick response unavailable when TextSecure is locked! Problem sending message! + + New TextSecure message + Contents hidden + OLD PASSPHRASE: NEW PASSPHRASE: @@ -754,6 +758,10 @@ Enabled Disabled + Name and message + Name only + Neither + %d hour @@ -858,6 +866,7 @@ \'WiFi Calling\' compatibility mode Enable if your device uses SMS/MMS delivery over WiFi (only enable when \'WiFi Calling\' is enabled on your device) Blocked contacts + Display in notifications diff --git a/res/xml/preferences_notifications.xml b/res/xml/preferences_notifications.xml index 5abc24ac93..c894abaf8b 100644 --- a/res/xml/preferences_notifications.xml +++ b/res/xml/preferences_notifications.xml @@ -36,7 +36,6 @@ android:entries="@array/pref_led_blink_pattern_entries" android:entryValues="@array/pref_led_blink_pattern_values" /> - + android:key="pref_repeat_alerts" + android:defaultValue="0" + android:title="@string/preferences__repeat_alerts" + android:dependency="pref_key_enable_notifications" + android:entries="@array/pref_repeat_alerts_entries" + android:entryValues="@array/pref_repeat_alerts_values" /> + + + \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java new file mode 100644 index 0000000000..1ad3f7d243 --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java @@ -0,0 +1,70 @@ +package org.thoughtcrime.securesms.notifications; + +import android.app.Notification; +import android.content.Context; +import android.graphics.Color; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase; +import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; + +public abstract class AbstractNotificationBuilder extends NotificationCompat.Builder { + + protected Context context; + protected NotificationPrivacyPreference privacy; + + public AbstractNotificationBuilder(Context context, NotificationPrivacyPreference privacy) { + super(context); + + this.context = context; + this.privacy = privacy; + } + + protected CharSequence getStyledMessage(@NonNull Recipient recipient, @Nullable CharSequence message) { + SpannableStringBuilder builder = new SpannableStringBuilder(); + builder.append(Util.getBoldedString(recipient.toShortString())); + builder.append(": "); + builder.append(message == null ? "" : message); + + return builder; + } + + public void setAlarms(@Nullable Uri ringtone, RecipientPreferenceDatabase.VibrateState vibrate) { + String defaultRingtoneName = TextSecurePreferences.getNotificationRingtone(context); + boolean defaultVibrate = TextSecurePreferences.isNotificationVibrateEnabled(context); + String ledColor = TextSecurePreferences.getNotificationLedColor(context); + String ledBlinkPattern = TextSecurePreferences.getNotificationLedPattern(context); + String ledBlinkPatternCustom = TextSecurePreferences.getNotificationLedPatternCustom(context); + String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom); + + if (ringtone != null) setSound(ringtone); + else if (TextUtils.isEmpty(defaultRingtoneName)) setSound(Uri.parse(defaultRingtoneName)); + + if (vibrate == RecipientPreferenceDatabase.VibrateState.ENABLED || + (vibrate == RecipientPreferenceDatabase.VibrateState.DEFAULT && defaultVibrate)) + { + setDefaults(Notification.DEFAULT_VIBRATE); + } + + if (!ledColor.equals("none")) { + setLights(Color.parseColor(ledColor), + Integer.parseInt(blinkPatternArray[0]), + Integer.parseInt(blinkPatternArray[1])); + } + } + + private String[] parseBlinkPattern(String blinkPattern, String blinkPatternCustom) { + if (blinkPattern.equals("custom")) + blinkPattern = blinkPatternCustom; + + return blinkPattern.split(","); + } +} diff --git a/src/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java new file mode 100644 index 0000000000..7491316a3f --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java @@ -0,0 +1,30 @@ +package org.thoughtcrime.securesms.notifications; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase; +import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; + +public class FailedNotificationBuilder extends AbstractNotificationBuilder { + + public FailedNotificationBuilder(Context context, NotificationPrivacyPreference privacy, Intent intent) { + super(context, privacy); + + setSmallIcon(R.drawable.icon_notification); + setLargeIcon(BitmapFactory.decodeResource(context.getResources(), + R.drawable.ic_action_warning_red)); + setContentTitle(context.getString(R.string.MessageNotifier_message_delivery_failed)); + setContentText(context.getString(R.string.MessageNotifier_failed_to_deliver_message)); + setTicker(context.getString(R.string.MessageNotifier_error_delivering_message)); + setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)); + setAutoCancel(true); + setAlarms(null, RecipientPreferenceDatabase.VibrateState.DEFAULT); + } + + + +} diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java index c324481f87..285f49acea 100644 --- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -17,43 +17,29 @@ package org.thoughtcrime.securesms.notifications; import android.app.AlarmManager; -import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationCompat.Action; -import android.support.v4.app.NotificationCompat.BigTextStyle; -import android.support.v4.app.NotificationCompat.InboxStyle; -import android.support.v4.app.RemoteInput; import android.text.Spannable; import android.text.SpannableString; -import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.StyleSpan; import android.util.Log; import org.thoughtcrime.securesms.ConversationActivity; -import org.thoughtcrime.securesms.ConversationListActivity; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.MessageRecord; @@ -61,7 +47,6 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.SpanUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; @@ -82,6 +67,7 @@ import me.leolin.shortcutbadger.ShortcutBadger; */ public class MessageNotifier { + private static final String TAG = MessageNotifier.class.getSimpleName(); public static final int NOTIFICATION_ID = 1338; @@ -101,19 +87,9 @@ public class MessageNotifier { Intent intent = new Intent(context, ConversationActivity.class); intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds()); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); - intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(context); - builder.setSmallIcon(R.drawable.icon_notification); - builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), - R.drawable.ic_action_warning_red)); - builder.setContentTitle(context.getString(R.string.MessageNotifier_message_delivery_failed)); - builder.setContentText(context.getString(R.string.MessageNotifier_failed_to_deliver_message)); - builder.setTicker(context.getString(R.string.MessageNotifier_error_delivering_message)); - builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)); - builder.setAutoCancel(true); - setNotificationAlarms(context, builder, true, null, VibrateState.DEFAULT); + intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); + FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent); ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) .notify((int)threadId, builder.build()); } @@ -187,13 +163,13 @@ public class MessageNotifier { } if (notificationState.hasMultipleThreads()) { - sendMultipleThreadNotification(context, masterSecret, notificationState, signal); + sendMultipleThreadNotification(context, notificationState, signal); } else { sendSingleThreadNotification(context, masterSecret, notificationState, signal); } updateBadge(context, notificationState.getMessageCount()); - scheduleReminder(context, masterSecret, reminderCount); + scheduleReminder(context, reminderCount); } finally { if (telcoCursor != null) telcoCursor.close(); if (pushCursor != null) pushCursor.close(); @@ -211,73 +187,33 @@ public class MessageNotifier { return; } - List notifications = notificationState.getNotifications(); - NotificationCompat.Builder builder = new NotificationCompat.Builder(context); - Recipients recipients = notifications.get(0).getRecipients(); - Recipient recipient = notifications.get(0).getIndividualRecipient(); - int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size); - Drawable recipientPhoto = recipient.getContactPhoto().asDrawable(context, recipients == null ? ContactColors.UNKNOWN_COLOR.toConversationColor(context) : - recipients.getColor().toConversationColor(context)); + SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + List notifications = notificationState.getNotifications(); - if (recipientPhoto != null) { - Bitmap recipientPhotoBitmap = BitmapUtil.createFromDrawable(recipientPhoto, largeIconTargetSize, largeIconTargetSize); - if (recipientPhotoBitmap != null) builder.setLargeIcon(recipientPhotoBitmap); - } - - builder.setSmallIcon(R.drawable.icon_notification); - builder.setColor(context.getResources().getColor(R.color.textsecure_primary)); - builder.setContentTitle(recipient.toShortString()); - builder.setContentText(notifications.get(0).getText()); + builder.setSender(notifications.get(0).getIndividualRecipient()); + builder.setMessageCount(notificationState.getMessageCount()); + builder.setPrimaryMessageBody(notifications.get(0).getText()); builder.setContentIntent(notifications.get(0).getPendingIntent(context)); - builder.setContentInfo(String.valueOf(notificationState.getMessageCount())); - builder.setPriority(NotificationCompat.PRIORITY_HIGH); - builder.setNumber(notificationState.getMessageCount()); - builder.setCategory(NotificationCompat.CATEGORY_MESSAGE); - builder.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(DeleteReceiver.DELETE_REMINDER_ACTION), 0)); - if (recipient.getContactUri() != null) builder.addPerson(recipient.getContactUri().toString()); long timestamp = notifications.get(0).getTimestamp(); if (timestamp != 0) builder.setWhen(timestamp); - if (masterSecret != null) { - Action markAsReadAction = new Action(R.drawable.check, - context.getString(R.string.MessageNotifier_mark_read), - notificationState.getMarkAsReadIntent(context)); - - Action replyAction = new Action(R.drawable.ic_reply_white_36dp, - context.getString(R.string.MessageNotifier_reply), - notificationState.getQuickReplyIntent(context, recipients)); - - Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, - context.getString(R.string.MessageNotifier_reply), - notificationState.getWearableReplyIntent(context, recipients)) - .addRemoteInput(new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(context.getString(R.string.MessageNotifier_reply)).build()) - .build(); - - builder.addAction(markAsReadAction); - builder.addAction(replyAction); - - builder.extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction) - .addAction(wearableReplyAction)); - } - - SpannableStringBuilder content = new SpannableStringBuilder(); + builder.addActions(masterSecret, + notificationState.getMarkAsReadIntent(context), + notificationState.getQuickReplyIntent(context, notifications.get(0).getRecipients()), + notificationState.getWearableReplyIntent(context, notifications.get(0).getRecipients())); ListIterator iterator = notifications.listIterator(notifications.size()); + while(iterator.hasPrevious()) { - NotificationItem item = iterator.previous(); - content.append(item.getBigStyleSummary()); - content.append('\n'); + builder.addMessageBody(iterator.previous().getText()); + } - builder.setStyle(new BigTextStyle().bigText(content)); - - setNotificationAlarms(context, builder, signal, - notificationState.getRingtone(), - notificationState.getVibrate()); - if (signal) { - builder.setTicker(notifications.get(0).getTickerText()); + builder.setAlarms(notificationState.getRingtone(), notificationState.getVibrate()); + builder.setTicker(notifications.get(0).getIndividualRecipient(), + notifications.get(0).getText()); } ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) @@ -285,59 +221,30 @@ public class MessageNotifier { } private static void sendMultipleThreadNotification(@NonNull Context context, - @Nullable MasterSecret masterSecret, @NonNull NotificationState notificationState, boolean signal) { - List notifications = notificationState.getNotifications(); - NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + List notifications = notificationState.getNotifications(); - builder.setColor(context.getResources().getColor(R.color.textsecure_primary)); - builder.setSmallIcon(R.drawable.icon_notification); - builder.setContentTitle(context.getString(R.string.app_name)); - builder.setSubText(context.getString(R.string.MessageNotifier_d_messages_in_d_conversations, - notificationState.getMessageCount(), - notificationState.getThreadCount())); - builder.setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s, - notifications.get(0).getIndividualRecipientName())); - builder.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0)); - - builder.setContentInfo(String.valueOf(notificationState.getMessageCount())); - builder.setNumber(notificationState.getMessageCount()); - builder.setCategory(NotificationCompat.CATEGORY_MESSAGE); + builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount()); + builder.setMostRecentSender(notifications.get(0).getIndividualRecipient()); long timestamp = notifications.get(0).getTimestamp(); if (timestamp != 0) builder.setWhen(timestamp); - builder.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(DeleteReceiver.DELETE_REMINDER_ACTION), 0)); - - if (masterSecret != null) { - Action markAllAsReadAction = new Action(R.drawable.check, - context.getString(R.string.MessageNotifier_mark_all_as_read), - notificationState.getMarkAsReadIntent(context)); - builder.addAction(markAllAsReadAction); - builder.extend(new NotificationCompat.WearableExtender().addAction(markAllAsReadAction)); - } - - InboxStyle style = new InboxStyle(); + builder.addActions(notificationState.getMarkAsReadIntent(context)); ListIterator iterator = notifications.listIterator(notifications.size()); + while(iterator.hasPrevious()) { NotificationItem item = iterator.previous(); - style.addLine(item.getTickerText()); - if (item.getIndividualRecipient().getContactUri() != null) { - builder.addPerson(item.getIndividualRecipient().getContactUri().toString()); - } + builder.addMessageBody(item.getIndividualRecipient(), item.getText()); } - builder.setStyle(style); - - setNotificationAlarms(context, builder, signal, - notificationState.getRingtone(), - notificationState.getVibrate()); - if (signal) { - builder.setTicker(notifications.get(0).getTickerText()); + builder.setAlarms(notificationState.getRingtone(), notificationState.getVibrate()); + builder.setTicker(notifications.get(0).getText()); } ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) @@ -411,7 +318,7 @@ public class MessageNotifier { body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (!recipients.isMuted()) { - notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null, 0)); + notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, 0)); } } } finally { @@ -436,7 +343,6 @@ public class MessageNotifier { Recipients recipients = record.getRecipients(); long threadId = record.getThreadId(); CharSequence body = record.getDisplayBody(); - Uri image = null; Recipients threadRecipients = null; long timestamp; @@ -458,7 +364,7 @@ public class MessageNotifier { } if (threadRecipients == null || !threadRecipients.isMuted()) { - notificationState.addNotification(new NotificationItem(recipient, recipients, threadRecipients, threadId, body, image, timestamp)); + notificationState.addNotification(new NotificationItem(recipient, recipients, threadRecipients, threadId, body, timestamp)); } } @@ -466,42 +372,6 @@ public class MessageNotifier { return notificationState; } - private static void setNotificationAlarms(Context context, - NotificationCompat.Builder builder, - boolean signal, - @Nullable Uri ringtone, - VibrateState vibrate) - - { - String defaultRingtoneName = TextSecurePreferences.getNotificationRingtone(context); - boolean defaultVibrate = TextSecurePreferences.isNotificationVibrateEnabled(context); - String ledColor = TextSecurePreferences.getNotificationLedColor(context); - String ledBlinkPattern = TextSecurePreferences.getNotificationLedPattern(context); - String ledBlinkPatternCustom = TextSecurePreferences.getNotificationLedPatternCustom(context); - String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom); - - if (signal && ringtone != null) builder.setSound(ringtone); - else if (signal && !TextUtils.isEmpty(defaultRingtoneName)) builder.setSound(Uri.parse(defaultRingtoneName)); - else builder.setSound(null); - - if (signal && (vibrate == VibrateState.ENABLED || (vibrate == VibrateState.DEFAULT && defaultVibrate))) { - builder.setDefaults(Notification.DEFAULT_VIBRATE); - } - - if (!ledColor.equals("none")) { - builder.setLights(Color.parseColor(ledColor), - Integer.parseInt(blinkPatternArray[0]), - Integer.parseInt(blinkPatternArray[1])); - } - } - - private static String[] parseBlinkPattern(String blinkPattern, String blinkPatternCustom) { - if (blinkPattern.equals("custom")) - blinkPattern = blinkPatternCustom; - - return blinkPattern.split(","); - } - private static void updateBadge(Context context, int count) { try { ShortcutBadger.setBadge(context.getApplicationContext(), count); @@ -512,7 +382,7 @@ public class MessageNotifier { } } - private static void scheduleReminder(Context context, MasterSecret masterSecret, int count) { + private static void scheduleReminder(Context context, int count) { if (count >= TextSecurePreferences.getRepeatAlertsCount(context)) { return; } diff --git a/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java new file mode 100644 index 0000000000..ace85d1405 --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java @@ -0,0 +1,84 @@ +package org.thoughtcrime.securesms.notifications; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; + +import org.thoughtcrime.securesms.ConversationListActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Util; + +import java.util.LinkedList; +import java.util.List; + +public class MultipleRecipientNotificationBuilder extends AbstractNotificationBuilder { + + private final List messageBodies = new LinkedList<>(); + + public MultipleRecipientNotificationBuilder(Context context, NotificationPrivacyPreference privacy) { + super(context, privacy); + + setColor(context.getResources().getColor(R.color.textsecure_primary)); + setSmallIcon(R.drawable.icon_notification); + setContentTitle(context.getString(R.string.app_name)); + setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0)); + setCategory(NotificationCompat.CATEGORY_MESSAGE); + setPriority(NotificationCompat.PRIORITY_HIGH); + setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(MessageNotifier.DeleteReceiver.DELETE_REMINDER_ACTION), 0)); + } + + public void setMessageCount(int messageCount, int threadCount) { + setSubText(context.getString(R.string.MessageNotifier_d_new_messages_in_d_conversations, + messageCount, threadCount)); + setContentInfo(String.valueOf(messageCount)); + setNumber(messageCount); + } + + public void setMostRecentSender(Recipient recipient) { + if (privacy.isDisplayContact()) { + setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s, + recipient.toShortString())); + } + } + + public void addActions(PendingIntent markAsReadIntent) { + NotificationCompat.Action markAllAsReadAction = new NotificationCompat.Action(R.drawable.check, + context.getString(R.string.MessageNotifier_mark_all_as_read), + markAsReadIntent); + addAction(markAllAsReadAction); + extend(new NotificationCompat.WearableExtender().addAction(markAllAsReadAction)); + } + + public void addMessageBody(@NonNull Recipient sender, @Nullable CharSequence body) { + if (privacy.isDisplayMessage()) { + messageBodies.add(getStyledMessage(sender, body)); + } else if (privacy.isDisplayContact()) { + messageBodies.add(Util.getBoldedString(sender.toShortString())); + } + + if (privacy.isDisplayContact() && sender.getContactUri() != null) { + addPerson(sender.getContactUri().toString()); + } + } + + @Override + public Notification build() { + if (privacy.isDisplayMessage() || privacy.isDisplayContact()) { + NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); + + for (CharSequence body : messageBodies) { + style.addLine(body); + } + + setStyle(style); + } + + return super.build(); + } +} diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationItem.java b/src/org/thoughtcrime/securesms/notifications/NotificationItem.java index 424bcf2932..8c45fc9270 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationItem.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationItem.java @@ -4,14 +4,10 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.support.annotation.Nullable; -import android.text.SpannableStringBuilder; import org.thoughtcrime.securesms.ConversationActivity; -import org.thoughtcrime.securesms.ConversationPopupActivity; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.util.Util; public class NotificationItem { @@ -20,18 +16,16 @@ public class NotificationItem { private final Recipients threadRecipients; private final long threadId; private final CharSequence text; - private final Uri image; private final long timestamp; public NotificationItem(Recipient individualRecipient, Recipients recipients, Recipients threadRecipients, long threadId, - CharSequence text, Uri image, long timestamp) + CharSequence text, long timestamp) { this.individualRecipient = individualRecipient; this.recipients = recipients; this.threadRecipients = threadRecipients; this.text = text; - this.image = image; this.threadId = threadId; this.timestamp = timestamp; } @@ -44,10 +38,6 @@ public class NotificationItem { return individualRecipient; } - public String getIndividualRecipientName() { - return individualRecipient.toShortString(); - } - public CharSequence getText() { return text; } @@ -56,31 +46,10 @@ public class NotificationItem { return timestamp; } - public Uri getImage() { - return image; - } - - public boolean hasImage() { - return image != null; - } - public long getThreadId() { return threadId; } - public CharSequence getBigStyleSummary() { - return (text == null) ? "" : text; - } - - public CharSequence getTickerText() { - SpannableStringBuilder builder = new SpannableStringBuilder(); - builder.append(Util.getBoldedString(getIndividualRecipientName())); - builder.append(": "); - builder.append(getText()); - - return builder; - } - public PendingIntent getPendingIntent(Context context) { Intent intent = new Intent(context, ConversationActivity.class); Recipients notifyRecipients = threadRecipients != null ? threadRecipients : recipients; diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java new file mode 100644 index 0000000000..8ac46008cf --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -0,0 +1,149 @@ +package org.thoughtcrime.securesms.notifications; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Action; +import android.support.v4.app.RemoteInput; +import android.text.SpannableStringBuilder; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.BitmapUtil; + +import java.util.LinkedList; +import java.util.List; + +public class SingleRecipientNotificationBuilder extends AbstractNotificationBuilder { + + private final List messageBodies = new LinkedList<>(); + + public SingleRecipientNotificationBuilder(@NonNull Context context, @NonNull NotificationPrivacyPreference privacy) { + super(context, privacy); + + setSmallIcon(R.drawable.icon_notification); + setColor(context.getResources().getColor(R.color.textsecure_primary)); + setPriority(NotificationCompat.PRIORITY_HIGH); + setCategory(NotificationCompat.CATEGORY_MESSAGE); + setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(MessageNotifier.DeleteReceiver.DELETE_REMINDER_ACTION), 0)); + } + + public void setSender(@NonNull Recipient recipient) { + if (privacy.isDisplayContact()) { + setContentTitle(recipient.toShortString()); + + if (recipient.getContactUri() != null) { + addPerson(recipient.getContactUri().toString()); + } + + setLargeIcon(recipient.getContactPhoto() + .asDrawable(context, recipient.getColor() + .toConversationColor(context))); + } else { + setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_new_textsecure_message)); + setLargeIcon(Recipient.getUnknownRecipient() + .getContactPhoto() + .asDrawable(context, Recipient.getUnknownRecipient() + .getColor() + .toConversationColor(context))); + } + } + + public void setMessageCount(int messageCount) { + setContentInfo(String.valueOf(messageCount)); + setNumber(messageCount); + } + + public void setPrimaryMessageBody(CharSequence message) { + if (privacy.isDisplayMessage()) { + setContentText(message); + } else { + setContentText(context.getString(R.string.SingleRecipientNotificationBuilder_contents_hidden)); + } + } + + public void addActions(@Nullable MasterSecret masterSecret, + @NonNull PendingIntent markReadIntent, + @NonNull PendingIntent quickReplyIntent, + @NonNull PendingIntent wearableReplyIntent) + { + Action markAsReadAction = new Action(R.drawable.check, + context.getString(R.string.MessageNotifier_mark_read), + markReadIntent); + + if (masterSecret != null) { + Action replyAction = new Action(R.drawable.ic_reply_white_36dp, + context.getString(R.string.MessageNotifier_reply), + quickReplyIntent); + + Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, + context.getString(R.string.MessageNotifier_reply), + wearableReplyIntent) + .addRemoteInput(new RemoteInput.Builder(MessageNotifier.EXTRA_VOICE_REPLY) + .setLabel(context.getString(R.string.MessageNotifier_reply)).build()) + .build(); + + addAction(markAsReadAction); + addAction(replyAction); + + extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction) + .addAction(wearableReplyAction)); + } else { + addAction(markAsReadAction); + + extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction)); + } + } + + public void addMessageBody(@Nullable CharSequence messageBody) { + if (privacy.isDisplayMessage()) { + messageBodies.add(messageBody == null ? "" : messageBody); + } + } + + public void setTicker(@NonNull Recipient recipient, @Nullable CharSequence message) { + if (privacy.isDisplayMessage()) { + setTicker(getStyledMessage(recipient, message)); + } else if (privacy.isDisplayContact()) { + setTicker(getStyledMessage(recipient, context.getString(R.string.SingleRecipientNotificationBuilder_new_textsecure_message))); + } else { + setTicker(context.getString(R.string.SingleRecipientNotificationBuilder_new_textsecure_message)); + } + } + + @Override + public Notification build() { + if (privacy.isDisplayMessage()) { + SpannableStringBuilder content = new SpannableStringBuilder(); + + for (CharSequence message : messageBodies) { + content.append(message); + content.append('\n'); + } + + setStyle(new NotificationCompat.BigTextStyle().bigText(content)); + } + + return super.build(); + } + + private void setLargeIcon(@Nullable Drawable drawable) { + if (drawable != null) { + int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size); + Bitmap recipientPhotoBitmap = BitmapUtil.createFromDrawable(drawable, largeIconTargetSize, largeIconTargetSize); + + if (recipientPhotoBitmap != null) { + setLargeIcon(recipientPhotoBitmap); + } + } + } + +} diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationPrivacyPreference.java b/src/org/thoughtcrime/securesms/preferences/NotificationPrivacyPreference.java new file mode 100644 index 0000000000..f2ff00db30 --- /dev/null +++ b/src/org/thoughtcrime/securesms/preferences/NotificationPrivacyPreference.java @@ -0,0 +1,19 @@ +package org.thoughtcrime.securesms.preferences; + +public class NotificationPrivacyPreference { + + private final String preference; + + public NotificationPrivacyPreference(String preference) { + this.preference = preference; + } + + public boolean isDisplayContact() { + return "all".equals(preference) || "contact".equals(preference); + } + + public boolean isDisplayMessage() { + return "all".equals(preference); + } + +} diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java index d9862f85d6..da5c6e8cdc 100644 --- a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java @@ -5,6 +5,7 @@ import android.content.SharedPreferences; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; @@ -14,13 +15,18 @@ import android.text.TextUtils; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.util.TextSecurePreferences; public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragment { + private MasterSecret masterSecret; + @Override public void onCreate(Bundle paramBundle) { super.onCreate(paramBundle); + masterSecret = getArguments().getParcelable("master_secret"); addPreferencesFromResource(R.xml.preferences_notifications); this.findPreference(TextSecurePreferences.LED_COLOR_PREF) @@ -31,10 +37,13 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme .setOnPreferenceChangeListener(new RingtoneSummaryListener()); this.findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF) .setOnPreferenceChangeListener(new ListSummaryListener()); + this.findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF) + .setOnPreferenceChangeListener(new NotificationPrivacyListener()); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_COLOR_PREF)); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_BLINK_PREF)); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF)); + initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)); initializeRingtoneSummary((RingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF)); } @@ -76,4 +85,20 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme return context.getString(TextSecurePreferences.isNotificationsEnabled(context) ? onCapsResId : offCapsResId); } + + private class NotificationPrivacyListener extends ListSummaryListener { + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + MessageNotifier.updateNotification(getActivity(), masterSecret); + return null; + } + }.execute(); + + return super.onPreferenceChange(preference, value); + } + + } } diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index ca9adc36c7..c5572da592 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -6,6 +6,8 @@ import android.preference.PreferenceManager; import android.provider.Settings; import android.util.Log; +import org.thoughtcrime.securesms.preferences.NotificationPrivacyPreference; + import java.io.IOException; public class TextSecurePreferences { @@ -70,6 +72,11 @@ public class TextSecurePreferences { private static final String RATING_ENABLED_PREF = "pref_rating_enabled"; public static final String REPEAT_ALERTS_PREF = "pref_repeat_alerts"; + public static final String NOTIFICATION_PRIVACY_PREF = "pref_notification_privacy"; + + public static NotificationPrivacyPreference getNotificationPrivacy(Context context) { + return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all")); + } public static long getRatingLaterTimestamp(Context context) { return getLongPreference(context, RATING_LATER_PREF, 0);