From df1217445880fc7b0a996667e08cdfb07f830672 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Thu, 11 Dec 2014 19:36:46 -0800 Subject: [PATCH] Add support for notification reminders. // FREEBIE Closes #1623 Fixes #323 --- AndroidManifest.xml | 12 +++- res/values/arrays.xml | 18 ++++++ res/xml/preferences_notifications.xml | 31 +++++---- .../notifications/MessageNotifier.java | 63 +++++++++++++++++-- .../NotificationsPreferenceFragment.java | 9 ++- .../securesms/util/TextSecurePreferences.java | 26 ++++++-- 6 files changed, 133 insertions(+), 26 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 764058ed68..11bdde1518 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -215,7 +215,6 @@ - + + + + + + + + + + + diff --git a/res/values/arrays.xml b/res/values/arrays.xml index a85647fe89..43de6671fb 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -184,4 +184,22 @@ @drawable/ic_send_sms_secure @drawable/ic_send_push + + + Never + One time + Two times + Three times + Five times + Ten times + + + + 0 + 1 + 2 + 3 + 5 + 10 + diff --git a/res/xml/preferences_notifications.xml b/res/xml/preferences_notifications.xml index 842ff72a8e..1aa50ae086 100644 --- a/res/xml/preferences_notifications.xml +++ b/res/xml/preferences_notifications.xml @@ -6,6 +6,19 @@ android:summary="@string/preferences__display_message_notifications_in_status_bar" android:defaultValue="true" /> + + + + - - + \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java index c850f7c7fc..d32aeb914b 100644 --- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -16,9 +16,11 @@ */ 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; @@ -40,23 +42,24 @@ import android.util.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.RoutingActivity; import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.PushDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; import java.io.IOException; import java.util.List; import java.util.ListIterator; +import java.util.concurrent.TimeUnit; -import me.leolin.shortcutbadger.ShortcutBadgeException; import me.leolin.shortcutbadger.ShortcutBadger; /** @@ -102,13 +105,12 @@ public class MessageNotifier { } } - public static void updateNotification(Context context, MasterSecret masterSecret) { if (!TextSecurePreferences.isNotificationsEnabled(context)) { return; } - updateNotification(context, masterSecret, false); + updateNotification(context, masterSecret, false, 0); } public static void updateNotification(Context context, MasterSecret masterSecret, long threadId) { @@ -120,11 +122,11 @@ public class MessageNotifier { DatabaseFactory.getThreadDatabase(context).setRead(threadId); sendInThreadNotification(context); } else { - updateNotification(context, masterSecret, true); + updateNotification(context, masterSecret, true, 0); } } - private static void updateNotification(Context context, MasterSecret masterSecret, boolean signal) { + private static void updateNotification(Context context, MasterSecret masterSecret, boolean signal, int reminderCount) { Cursor telcoCursor = null; Cursor pushCursor = null; @@ -138,6 +140,7 @@ public class MessageNotifier { ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) .cancel(NOTIFICATION_ID); updateBadge(context, 0); + clearReminder(context); return; } @@ -152,6 +155,7 @@ public class MessageNotifier { } updateBadge(context, notificationState.getMessageCount()); + scheduleReminder(context, masterSecret, reminderCount); } finally { if (telcoCursor != null) telcoCursor.close(); if (pushCursor != null) pushCursor.close(); @@ -180,6 +184,7 @@ public class MessageNotifier { builder.setContentIntent(notifications.get(0).getPendingIntent(context)); builder.setContentInfo(String.valueOf(notificationState.getMessageCount())); builder.setNumber(notificationState.getMessageCount()); + builder.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(DeleteReceiver.DELETE_REMINDER_ACTION), 0)); if (masterSecret != null) { builder.addAction(R.drawable.check, context.getString(R.string.MessageNotifier_mark_as_read), @@ -227,6 +232,8 @@ public class MessageNotifier { builder.setContentInfo(String.valueOf(notificationState.getMessageCount())); builder.setNumber(notificationState.getMessageCount()); + builder.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(DeleteReceiver.DELETE_REMINDER_ACTION), 0)); + if (masterSecret != null) { builder.addAction(R.drawable.check, context.getString(R.string.MessageNotifier_mark_all_as_read), notificationState.getMarkAsReadIntent(context, masterSecret)); @@ -400,4 +407,48 @@ public class MessageNotifier { Log.w("MessageNotifier", t); } } + + private static void scheduleReminder(Context context, MasterSecret masterSecret, int count) { + if (count >= TextSecurePreferences.getRepeatAlertsCount(context)) { + return; + } + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); + alarmIntent.putExtra("reminder_count", count); + + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + long timeout = TimeUnit.SECONDS.toMillis(10); + + alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); + } + + private static void clearReminder(Context context) { + Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmManager.cancel(pendingIntent); + } + + public static class ReminderReceiver extends BroadcastReceiver { + + public static final String REMINDER_ACTION = "org.thoughtcrime.securesms.MessageNotifier.REMINDER_ACTION"; + + @Override + public void onReceive(Context context, Intent intent) { + MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); + int reminderCount = intent.getIntExtra("reminder_count", 0); + MessageNotifier.updateNotification(context, masterSecret, true, reminderCount + 1); + } + } + + public static class DeleteReceiver extends BroadcastReceiver { + + public static final String DELETE_REMINDER_ACTION = "org.thoughtcrime.securesms.MessageNotifier.DELETE_REMINDER_ACTION"; + + @Override + public void onReceive(Context context, Intent intent) { + clearReminder(context); + } + } } diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java index dc4b6b67e5..f395a5968e 100644 --- a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java @@ -28,14 +28,17 @@ public class NotificationsPreferenceFragment extends PreferenceFragment { addPreferencesFromResource(R.xml.preferences_notifications); this.findPreference(TextSecurePreferences.LED_COLOR_PREF) - .setOnPreferenceChangeListener(new ListSummaryListener()); + .setOnPreferenceChangeListener(new ListSummaryListener()); this.findPreference(TextSecurePreferences.LED_BLINK_PREF) - .setOnPreferenceChangeListener(new ListSummaryListener()); + .setOnPreferenceChangeListener(new ListSummaryListener()); this.findPreference(TextSecurePreferences.RINGTONE_PREF) - .setOnPreferenceChangeListener(new RingtoneSummaryListener()); + .setOnPreferenceChangeListener(new RingtoneSummaryListener()); + this.findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF) + .setOnPreferenceChangeListener(new ListSummaryListener()); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_COLOR_PREF)); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_BLINK_PREF)); + initializeListSummary((ListPreference) findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF)); initializeRingtoneSummary((RingtonePreference) findPreference(TextSecurePreferences.RINGTONE_PREF)); } diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 8b6d084eb7..63ce32a658 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -8,6 +8,8 @@ import java.io.IOException; public class TextSecurePreferences { + private static final String TAG = TextSecurePreferences.class.getSimpleName(); + public static final String IDENTITY_PREF = "pref_choose_identity"; public static final String CHANGE_PASSPHRASE_PREF = "pref_change_passphrase"; public static final String DISABLE_PASSPHRASE_PREF = "pref_disable_passphrase"; @@ -56,6 +58,25 @@ public class TextSecurePreferences { private static final String FALLBACK_MMS_ENABLED_PREF = "pref_mms_fallback_enabled"; private static final String SIGNED_PREKEY_REGISTERED_PREF = "pref_signed_prekey_registered"; + private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id"; + private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version"; + + private static final String PUSH_REGISTRATION_REMINDER_PREF = "pref_push_registration_reminder"; + public static final String REPEAT_ALERTS_PREF = "pref_repeat_alerts"; + + public static int getRepeatAlertsCount(Context context) { + try { + return Integer.parseInt(getStringPreference(context, REPEAT_ALERTS_PREF, "0")); + } catch (NumberFormatException e) { + Log.w(TAG, e); + return 0; + } + } + + public static void setRepeatAlertsCount(Context context, int count) { + setStringPreference(context, REPEAT_ALERTS_PREF, String.valueOf(count)); + } + public static boolean isSignedPreKeyRegistered(Context context) { return getBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, false); } @@ -64,11 +85,6 @@ public class TextSecurePreferences { setBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, value); } - private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id"; - private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version"; - - private static final String PUSH_REGISTRATION_REMINDER_PREF = "pref_push_registration_reminder"; - public static void setGcmRegistrationId(Context context, String registrationId) { setStringPreference(context, GCM_REGISTRATION_ID_PREF, registrationId); setIntegerPrefrence(context, GCM_REGISTRATION_ID_VERSION_PREF, Util.getCurrentApkReleaseVersion(context));