From 7dc0929ffed3040d89deb02dc2d731873a14e51b Mon Sep 17 00:00:00 2001 From: M66B Date: Tue, 24 Sep 2019 09:34:43 +0200 Subject: [PATCH] Added notification snooze action --- app/src/main/java/eu/faircode/email/Core.java | 13 +++ .../java/eu/faircode/email/EntityMessage.java | 4 +- .../email/FragmentOptionsNotifications.java | 39 +++++++- .../java/eu/faircode/email/ServiceUI.java | 94 +++++++++++++------ .../layout/fragment_options_notifications.xml | 32 ++++++- app/src/main/res/values/strings.xml | 2 + 6 files changed, 152 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index 5d5bfc396b..d19e06006e 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -2382,6 +2382,7 @@ class Core { boolean notify_reply_direct = (prefs.getBoolean("notify_reply_direct", false) && pro); boolean notify_flag = (prefs.getBoolean("notify_flag", false) && flags && pro); boolean notify_seen = (prefs.getBoolean("notify_seen", true) || !pro); + boolean notify_snooze = (prefs.getBoolean("notify_snooze", false) || !pro); boolean light = prefs.getBoolean("light", false); String sound = prefs.getString("sound", null); boolean alert_once = prefs.getBoolean("alert_once", true); @@ -2634,6 +2635,18 @@ class Core { mbuilder.addAction(actionSeen.build()); } + if (notify_snooze) { + Intent snooze = new Intent(context, ServiceUI.class) + .setAction("snooze:" + message.id) + .putExtra("group", group); + PendingIntent piSnooze = PendingIntent.getService(context, ServiceUI.PI_SNOOZE, snooze, PendingIntent.FLAG_UPDATE_CURRENT); + NotificationCompat.Action.Builder actionSnooze = new NotificationCompat.Action.Builder( + R.drawable.baseline_timelapse_24, + context.getString(R.string.title_advanced_notify_action_snooze), + piSnooze); + mbuilder.addAction(actionSnooze.build()); + } + if (!biometrics || biometric_notify) { if (!TextUtils.isEmpty(message.subject)) mbuilder.setContentText(message.subject); diff --git a/app/src/main/java/eu/faircode/email/EntityMessage.java b/app/src/main/java/eu/faircode/email/EntityMessage.java index 99d3d904c1..91153ee693 100644 --- a/app/src/main/java/eu/faircode/email/EntityMessage.java +++ b/app/src/main/java/eu/faircode/email/EntityMessage.java @@ -228,8 +228,8 @@ public class EntityMessage implements Serializable { static void snooze(Context context, long id, Long wakeup) { Intent snoozed = new Intent(context, ServiceUI.class); - snoozed.setAction("snooze:" + id); - PendingIntent pi = PendingIntent.getService(context, ServiceUI.PI_SNOOZED, snoozed, PendingIntent.FLAG_UPDATE_CURRENT); + snoozed.setAction("wakeup:" + id); + PendingIntent pi = PendingIntent.getService(context, ServiceUI.PI_WAKEUP, snoozed, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (wakeup == null) { diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java index c5508ef702..daf3d00d06 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java @@ -28,6 +28,8 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import android.text.Editable; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -37,6 +39,7 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; @@ -60,6 +63,8 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared private CheckBox cbNotifyActionReplyDirect; private CheckBox cbNotifyActionFlag; private CheckBox cbNotifyActionSeen; + private CheckBox cbNotifyActionSnooze; + private EditText etNotifyActionSnooze; private TextView tvNotifyActionsPro; private SwitchCompat swNotifyRemove; private SwitchCompat swBiometricsNotify; @@ -74,7 +79,8 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared private final static String[] RESET_OPTIONS = new String[]{ "badge", "unseen_ignored", - "notify_preview", "notify_trash", "notify_archive", "notify_reply", "notify_reply_direct", "notify_flag", "notify_seen", "notify_remove", + "notify_preview", "notify_trash", "notify_archive", "notify_reply", "notify_reply_direct", "notify_flag", + "notify_seen", "notify_snooze", "notify_snooze_duration", "notify_remove", "biometrics_notify", "light", "sound", "alert_once" }; @@ -98,6 +104,8 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared cbNotifyActionReplyDirect = view.findViewById(R.id.cbNotifyActionReplyDirect); cbNotifyActionFlag = view.findViewById(R.id.cbNotifyActionFlag); cbNotifyActionSeen = view.findViewById(R.id.cbNotifyActionSeen); + cbNotifyActionSnooze = view.findViewById(R.id.cbNotifyActionSnooze); + etNotifyActionSnooze = view.findViewById(R.id.etNotifyActionSnooze); tvNotifyActionsPro = view.findViewById(R.id.tvNotifyActionsPro); swNotifyRemove = view.findViewById(R.id.swNotifyRemove); swBiometricsNotify = view.findViewById(R.id.swBiometricsNotify); @@ -182,6 +190,32 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared } }); + cbNotifyActionSnooze.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean checked) { + prefs.edit().putBoolean("notify_snooze", checked).apply(); + } + }); + + etNotifyActionSnooze.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable editable) { + try { + int minutes = Integer.parseInt(editable.toString()); + prefs.edit().putInt("notify_snooze_duration", minutes).apply(); + } catch (NumberFormatException ex) { + } + } + }); + Helper.linkPro(tvNotifyActionsPro); swNotifyRemove.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @@ -309,12 +343,15 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared cbNotifyActionReplyDirect.setChecked(prefs.getBoolean("notify_reply_direct", false) && pro); cbNotifyActionFlag.setChecked(prefs.getBoolean("notify_flag", false) && pro); cbNotifyActionSeen.setChecked(prefs.getBoolean("notify_seen", true) || !pro); + cbNotifyActionSnooze.setChecked(prefs.getBoolean("notify_snooze", false) || !pro); + etNotifyActionSnooze.setText(Integer.toString(prefs.getInt("notify_snooze_duration", 60))); cbNotifyActionTrash.setEnabled(pro); cbNotifyActionArchive.setEnabled(pro); cbNotifyActionReply.setEnabled(pro); cbNotifyActionFlag.setEnabled(pro); cbNotifyActionSeen.setEnabled(pro); + cbNotifyActionSnooze.setEnabled(pro); swNotifyRemove.setChecked(prefs.getBoolean("notify_remove", true)); swBiometricsNotify.setChecked(prefs.getBoolean("biometrics_notify", false)); diff --git a/app/src/main/java/eu/faircode/email/ServiceUI.java b/app/src/main/java/eu/faircode/email/ServiceUI.java index e4621c0e68..e9c7e41a32 100644 --- a/app/src/main/java/eu/faircode/email/ServiceUI.java +++ b/app/src/main/java/eu/faircode/email/ServiceUI.java @@ -46,8 +46,9 @@ public class ServiceUI extends IntentService { static final int PI_REPLY_DIRECT = 4; static final int PI_FLAG = 5; static final int PI_SEEN = 6; - static final int PI_IGNORED = 7; - static final int PI_SNOOZED = 8; + static final int PI_SNOOZE = 7; + static final int PI_IGNORED = 8; + static final int PI_WAKEUP = 9; public ServiceUI() { this(ServiceUI.class.getName()); @@ -120,16 +121,21 @@ public class ServiceUI extends IntentService { onSeen(id); break; + case "snooze": + cancel(group, id); + onSnooze(id); + break; + case "ignore": onIgnore(id); break; - case "snooze": + case "wakeup": // AlarmManager.RTC_WAKEUP // When the alarm is dispatched, the app will also be added to the system's temporary whitelist // for approximately 10 seconds to allow that application to acquire further wake locks in which to complete its work. // https://developer.android.com/reference/android/app/AlarmManager - onSnooze(id); + onWakeup(id); break; default: throw new IllegalArgumentException("Unknown UI action: " + parts[0]); @@ -158,11 +164,12 @@ public class ServiceUI extends IntentService { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - if (message != null) { - EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TRASH); - if (trash != null) - EntityOperation.queue(this, message, EntityOperation.MOVE, trash.id); - } + if (message == null) + return; + + EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TRASH); + if (trash != null) + EntityOperation.queue(this, message, EntityOperation.MOVE, trash.id); db.setTransactionSuccessful(); } finally { @@ -176,13 +183,14 @@ public class ServiceUI extends IntentService { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - if (message != null) { - EntityFolder archive = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE); - if (archive == null) - archive = db.folder().getFolderByType(message.account, EntityFolder.TRASH); - if (archive != null) - EntityOperation.queue(this, message, EntityOperation.MOVE, archive.id); - } + if (message == null) + return; + + EntityFolder archive = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE); + if (archive == null) + archive = db.folder().getFolderByType(message.account, EntityFolder.TRASH); + if (archive != null) + EntityOperation.queue(this, message, EntityOperation.MOVE, archive.id); db.setTransactionSuccessful(); } finally { @@ -262,13 +270,14 @@ public class ServiceUI extends IntentService { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - if (message != null) { - List messages = db.message().getMessagesByThread( - message.account, message.thread, threading ? null : id, null); - for (EntityMessage threaded : messages) { - EntityOperation.queue(this, threaded, EntityOperation.FLAG, true); - EntityOperation.queue(this, threaded, EntityOperation.SEEN, true); - } + if (message == null) + return; + + List messages = db.message().getMessagesByThread( + message.account, message.thread, threading ? null : id, null); + for (EntityMessage threaded : messages) { + EntityOperation.queue(this, threaded, EntityOperation.FLAG, true); + EntityOperation.queue(this, threaded, EntityOperation.SEEN, true); } db.setTransactionSuccessful(); @@ -283,10 +292,37 @@ public class ServiceUI extends IntentService { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - if (message != null) - EntityOperation.queue(this, message, EntityOperation.SEEN, true); + if (message == null) + return; + + EntityOperation.queue(this, message, EntityOperation.SEEN, true); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + private void onSnooze(long id) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + int notify_snooze_duration = prefs.getInt("notify_snooze_duration", 60); + + long wakeup = new Date().getTime() + notify_snooze_duration * 60 * 1000L; + + DB db = DB.getInstance(this); + try { + db.beginTransaction(); + + EntityMessage message = db.message().getMessage(id); + if (message == null) + return; + + db.message().setMessageSnoozed(id, wakeup); + EntityOperation.queue(this, message, EntityOperation.SEEN, true); + EntityMessage.snooze(this, id, wakeup); db.setTransactionSuccessful(); + } finally { db.endTransaction(); } @@ -303,8 +339,10 @@ public class ServiceUI extends IntentService { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - if (message != null) - db.message().setMessageUiIgnored(message.id, true); + if (message == null) + return; + + db.message().setMessageUiIgnored(message.id, true); db.setTransactionSuccessful(); } finally { @@ -312,7 +350,7 @@ public class ServiceUI extends IntentService { } } - private void onSnooze(long id) { + private void onWakeup(long id) { DB db = DB.getInstance(this); try { db.beginTransaction(); diff --git a/app/src/main/res/layout/fragment_options_notifications.xml b/app/src/main/res/layout/fragment_options_notifications.xml index 082e516095..2cb56fbab8 100644 --- a/app/src/main/res/layout/fragment_options_notifications.xml +++ b/app/src/main/res/layout/fragment_options_notifications.xml @@ -137,6 +137,36 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbNotifyActionFlag" /> + + + + + + + app:layout_constraintTop_toBottomOf="@id/etNotifyActionSnooze" /> Direct reply Star Read + Snooze Remove new message notification on tapping on notification Show notification content when using biometric authentication Use notification light @@ -831,6 +832,7 @@ Now After %1$s Reset + Minutes Accept Decline