Fix notification summary issues, add snooze all

pull/574/head
Alex Baker 7 years ago
parent a85fce6b70
commit 328a7603fa

@ -205,7 +205,6 @@ public class PhoneStateChangedReceiver extends InjectingBroadcastReceiver {
.setWhen(currentTimeMillis())
.setShowWhen(true)
.setSmallIcon(R.drawable.ic_check_white_24dp)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(PendingIntent.getActivity(context, missedCallDialog.hashCode(), missedCallDialog, PendingIntent.FLAG_UPDATE_CURRENT));
Bitmap contactImage = getContactImage(contactId);

@ -35,6 +35,7 @@ import org.tasks.ui.TimePreference;
import javax.inject.Inject;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybean;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastMarshmallow;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastOreo;
import static com.todoroo.andlib.utility.AndroidUtilities.preOreo;
@ -87,6 +88,7 @@ public class ReminderPreferences extends InjectingPreferenceActivity {
requires(atLeastOreo(), R.string.notification_channel_settings);
requires(atLeastMarshmallow(), R.string.battery_optimization_settings);
requires(preOreo(), R.string.p_rmd_ringtone, R.string.p_rmd_vibrate, R.string.p_led_notification);
requires(atLeastJellybean(), R.string.p_bundle_notifications);
}
@TargetApi(Build.VERSION_CODES.O)

@ -22,10 +22,9 @@ import org.tasks.notifications.TelephonyManager;
import org.tasks.preferences.Preferences;
import org.tasks.ui.CheckBoxes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@ -93,7 +92,6 @@ public class Notifier {
.setContentText(filter.listingTitle)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setWhen(currentTimeMillis())
.setShowWhen(true)
.setColor(checkBoxes.getPriorityColor(maxPriority))
@ -117,8 +115,6 @@ public class Notifier {
triggerNotifications(Collections.singletonList(notification), true);
}
public void restoreNotifications() {
triggerNotifications(notificationDao.getAll(), false);
}
@ -128,7 +124,7 @@ public class Notifier {
}
private void triggerNotifications(List<org.tasks.notifications.Notification> entries, boolean alert) {
Map<org.tasks.notifications.Notification, NotificationCompat.Builder> notifications = new LinkedHashMap<>();
List<org.tasks.notifications.Notification> notifications = new ArrayList<>();
boolean ringFiveTimes = false;
boolean ringNonstop = false;
for (int i = 0 ; i < entries.size() ; i++) {
@ -143,17 +139,14 @@ public class Notifier {
}
NotificationCompat.Builder notification = notificationManager.getTaskNotification(entry);
if (notification != null) {
notification.setGroupAlertBehavior(alert && (preferences.bundleNotifications() ? entries.size() == 1 : i == entries.size() - 1)
? NotificationCompat.GROUP_ALERT_CHILDREN
: NotificationCompat.GROUP_ALERT_SUMMARY);
notifications.put(entry, notification);
notifications.add(entry);
}
}
if (notifications.isEmpty()) {
return;
} else {
Timber.d("Triggering %s", notifications.keySet());
Timber.d("Triggering %s", notifications);
}
notificationManager.notifyTasks(notifications, alert, ringNonstop, ringFiveTimes);
@ -163,9 +156,9 @@ public class Notifier {
!ringNonstop &&
!audioManager.notificationsMuted() &&
telephonyManager.callStateIdle()) {
for (NotificationCompat.Builder notification : notifications.values()) {
for (org.tasks.notifications.Notification notification : notifications) {
AndroidUtilities.sleepDeep(2000);
voiceOutputAssistant.speak(notification.build().tickerText.toString());
voiceOutputAssistant.speak(notificationManager.getTaskNotification(notification).build().tickerText.toString());
}
}
}

@ -25,7 +25,7 @@ public interface NotificationDao {
int delete(long taskId);
@Query("DELETE FROM notification WHERE task IN(:taskIds)")
void deleteAll(List<Long> taskIds);
int deleteAll(List<Long> taskIds);
@Query("SELECT MAX(timestamp) FROM notification")
long latestTimestamp();

@ -33,8 +33,8 @@ import org.tasks.reminders.SnoozeOption;
import org.tasks.ui.CheckBoxes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@ -45,11 +45,13 @@ import timber.log.Timber;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.tryFind;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.transform;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybean;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastOreo;
import static com.todoroo.andlib.utility.AndroidUtilities.preLollipop;
@ApplicationScope
public class NotificationManager {
@ -110,16 +112,7 @@ public class NotificationManager {
}
notificationDao.deleteAll(tasks);
} else if (notificationDao.delete(id) > 0) {
if (notificationDao.count() == 1) {
List<org.tasks.notifications.Notification> notifications = notificationDao.getAll();
org.tasks.notifications.Notification notification = notifications.get(0);
NotificationCompat.Builder builder = getTaskNotification(notification)
.setGroup(null)
.setOnlyAlertOnce(true)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
notify(notification.taskId, builder, false, false, false);
}
updateSummary(false, false, false);
notifyTasks(Collections.emptyList(), false, false, false);
}
})
.observeOn(AndroidSchedulers.mainThread())
@ -127,118 +120,147 @@ public class NotificationManager {
.subscribe();
}
public void notifyTasks(Map<org.tasks.notifications.Notification, NotificationCompat.Builder> notifications, boolean alert, boolean nonstop, boolean fiveTimes) {
List<org.tasks.notifications.Notification> existing = notificationDao.getAll();
if (existing.size() == 1) {
org.tasks.notifications.Notification notification = existing.get(0);
NotificationCompat.Builder builder = getTaskNotification(notification)
.setGroup(GROUP_KEY)
.setOnlyAlertOnce(true)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
notify(notification.taskId, builder, false, false, false);
}
notificationDao.insertAll(newArrayList(notifications.keySet()));
updateSummary(alert && notifications.size() > 1, nonstop, fiveTimes);
ArrayList<Map.Entry<org.tasks.notifications.Notification, NotificationCompat.Builder>> entries = newArrayList(notifications.entrySet());
int last = entries.size() - 1;
for (int i = 0; i <= last; i++) {
Map.Entry<org.tasks.notifications.Notification, NotificationCompat.Builder> entry = entries.get(i);
long taskId = entry.getKey().taskId;
Task task = taskDao.fetch(taskId);
NotificationCompat.Builder builder = entry.getValue();
builder.setColor(checkBoxes.getPriorityColor(task.getImportance()));
if (i < last) {
notify(taskId, builder, false, false, false);
} else {
notify(taskId, builder, alert, nonstop, fiveTimes);
public void cancel(List<Long> ids) {
Completable.fromAction(() -> {
if (notificationDao.deleteAll(ids) > 0) {
notifyTasks(Collections.emptyList(), false, false, false);
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe();
}
public void notify(long notificationId, NotificationCompat.Builder builder, boolean alert, boolean nonstop, boolean fiveTimes) {
Notification notification = builder.build();
if (preferences.getBoolean(R.string.p_rmd_enabled, true)) {
int ringTimes = 1;
if (preferences.getBoolean(R.string.p_rmd_persistent, true)) {
notification.flags |= Notification.FLAG_NO_CLEAR;
public void notifyTasks(List<org.tasks.notifications.Notification> newNotifications, boolean alert, boolean nonstop, boolean fiveTimes) {
notificationDao.insertAll(newNotifications);
List<org.tasks.notifications.Notification> notifications = notificationDao.getAllOrdered();
int totalCount = notifications.size();
if (totalCount == 0) {
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID);
} else if (totalCount == 1) {
org.tasks.notifications.Notification notification = notifications.get(0);
NotificationCompat.Builder builder = getTaskNotification(notification);
if (builder != null) {
notify(notification.taskId, builder, alert, nonstop, fiveTimes);
}
if (preferences.isLEDNotificationEnabled()) {
notification.defaults |= Notification.DEFAULT_LIGHTS;
}
if (alert) {
if (nonstop) {
notification.flags |= Notification.FLAG_INSISTENT;
ringTimes = 1;
} else if (fiveTimes) {
ringTimes = 5;
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID);
} else if (preferences.bundleNotifications()) {
updateSummary(false, false, false);
for (org.tasks.notifications.Notification notification : notifications) {
NotificationCompat.Builder builder = getTaskNotification(notification);
if (builder != null) {
builder.setGroup(GROUP_KEY)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
.setPriority(NotificationCompat.PRIORITY_MIN);
notify(notification.taskId, builder, false, false, false);
}
if (preferences.isVibrationEnabled()) {
notification.defaults |= Notification.DEFAULT_VIBRATE;
}
if (newNotifications.size() == 1) {
org.tasks.notifications.Notification notification = newNotifications.get(0);
NotificationCompat.Builder builder = getTaskNotification(notification);
if (builder != null) {
builder.setGroup(GROUP_KEY)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
.setPriority(NotificationCompat.PRIORITY_HIGH);
notify(notification.taskId, builder, alert, nonstop, fiveTimes);
}
notification.sound = preferences.getRingtone();
notification.audioStreamType = Notification.STREAM_DEFAULT;
} else {
updateSummary(alert, nonstop, fiveTimes);
}
Intent deleteIntent = new Intent(context, NotificationClearedReceiver.class);
deleteIntent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
notification.deleteIntent = PendingIntent.getBroadcast(context, (int) notificationId, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);
for (int i = 0 ; i < ringTimes ; i++) {
notificationManagerCompat.notify((int) notificationId, notification);
} else {
for (org.tasks.notifications.Notification notification : newNotifications) {
NotificationCompat.Builder builder = getTaskNotification(notification);
if (builder != null) {
notify(notification.taskId, builder, alert, nonstop, fiveTimes);
alert = false;
}
}
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID);
}
}
public void notify(long notificationId, NotificationCompat.Builder builder, boolean alert, boolean nonstop, boolean fiveTimes) {
if (!preferences.getBoolean(R.string.p_rmd_enabled, true)) {
return;
}
int ringTimes = fiveTimes ? 5 : 1;
builder.setOngoing(preferences.usePersistentReminders());
if (alert) {
builder.setSound(preferences.getRingtone())
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(preferences.getNotificationDefaults());
} else {
builder.setDefaults(0)
.setTicker(null);
}
Notification notification = builder.build();
if (alert && nonstop) {
notification.flags |= Notification.FLAG_INSISTENT;
ringTimes = 1;
}
Intent deleteIntent = new Intent(context, NotificationClearedReceiver.class);
deleteIntent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
notification.deleteIntent = PendingIntent.getBroadcast(context, (int) notificationId, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);
for (int i = 0 ; i < ringTimes ; i++) {
notificationManagerCompat.notify((int) notificationId, notification);
}
}
private void updateSummary(boolean notify, boolean nonStop, boolean fiveTimes) {
if (preferences.bundleNotifications()) {
int taskCount = notificationDao.count();
if (taskCount <= 1) {
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID);
} else {
List<org.tasks.notifications.Notification> notifications = notificationDao.getAllOrdered();
List<Long> notificationIds = transform(notifications, n -> n.taskId);
QueryTemplate query = new QueryTemplate().where(Task.ID.in(notificationIds));
Filter filter = new Filter(context.getString(R.string.notifications), query);
List<Task> tasks = taskDao.toList(Query.select(Task.PROPERTIES)
.withQueryTemplate(query.toString()));
long when = notificationDao.latestTimestamp();
int maxPriority = 3;
String summaryTitle = context.getString(R.string.task_count, taskCount);
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle()
.setBigContentTitle(summaryTitle);
for (org.tasks.notifications.Notification notification : notifications) {
Task task = tryFind(tasks, t -> t.getId() == notification.taskId).orNull();
if (task == null) {
continue;
}
style.addLine(task.getTitle());
maxPriority = Math.min(maxPriority, task.getImportance());
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationManager.NOTIFICATION_CHANNEL_DEFAULT)
.setContentTitle(summaryTitle)
.setContentText(context.getString(R.string.app_name))
.setGroupSummary(true)
.setGroup(GROUP_KEY)
.setShowWhen(true)
.setWhen(when)
.setSmallIcon(R.drawable.ic_done_all_white_24dp)
.setStyle(style)
.setColor(checkBoxes.getPriorityColor(maxPriority))
.setNumber(taskCount)
.setContentIntent(PendingIntent.getActivity(context, 0, TaskIntents.getTaskListIntent(context, filter), PendingIntent.FLAG_UPDATE_CURRENT));
if (notify) {
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSound(preferences.getRingtone());
} else {
builder.setOnlyAlertOnce(true)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
}
if (!preferences.bundleNotifications()) {
return;
}
notify(NotificationManager.SUMMARY_NOTIFICATION_ID, builder, notify, nonStop, fiveTimes);
List<org.tasks.notifications.Notification> notifications = notificationDao.getAllOrdered();
int taskCount = notifications.size();
ArrayList<Long> taskIds = newArrayList(transform(notifications, n -> n.taskId));
QueryTemplate query = new QueryTemplate().where(Task.ID.in(taskIds));
Filter filter = new Filter(context.getString(R.string.notifications), query);
List<Task> tasks = taskDao.toList(Query.select(Task.PROPERTIES)
.withQueryTemplate(query.toString()));
long when = notificationDao.latestTimestamp();
int maxPriority = 3;
String summaryTitle = context.getString(R.string.task_count, taskCount);
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle()
.setBigContentTitle(summaryTitle);
for (org.tasks.notifications.Notification notification : notifications) {
Task task = tryFind(tasks, t -> t.getId() == notification.taskId).orNull();
if (task == null) {
continue;
}
} else {
notificationManagerCompat.cancel(NotificationManager.SUMMARY_NOTIFICATION_ID);
style.addLine(task.getTitle());
maxPriority = Math.min(maxPriority, task.getImportance());
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationManager.NOTIFICATION_CHANNEL_DEFAULT)
.setContentTitle(summaryTitle)
.setContentText(context.getString(R.string.app_name))
.setShowWhen(true)
.setWhen(when)
.setSmallIcon(R.drawable.ic_done_all_white_24dp)
.setStyle(style)
.setColor(checkBoxes.getPriorityColor(maxPriority))
.setNumber(taskCount)
.setOnlyAlertOnce(false)
.setContentIntent(PendingIntent.getActivity(context, 0, TaskIntents.getTaskListIntent(context, filter), PendingIntent.FLAG_UPDATE_CURRENT))
.setGroupSummary(true)
.setGroup(GROUP_KEY)
.setGroupAlertBehavior(notify ? NotificationCompat.GROUP_ALERT_SUMMARY : NotificationCompat.GROUP_ALERT_CHILDREN);
if (preLollipop()) {
builder.setTicker(summaryTitle);
}
Intent snoozeIntent = new Intent(context, SnoozeActivity.class);
snoozeIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
snoozeIntent.putExtra(SnoozeActivity.EXTRA_TASK_IDS, taskIds);
builder.addAction(
R.drawable.ic_snooze_white_24dp,
context.getResources().getString(R.string.snooze_all),
PendingIntent.getActivity(context, 0, snoozeIntent, PendingIntent.FLAG_CANCEL_CURRENT));
notify(NotificationManager.SUMMARY_NOTIFICATION_ID, builder, notify, nonStop, fiveTimes);
}
public NotificationCompat.Builder getTaskNotification(org.tasks.notifications.Notification notification) {
@ -282,18 +304,19 @@ public class NotificationManager {
task.setReminderLast(when);
taskDao.saveExisting(task);
final String appName = context.getString(R.string.app_name);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationManager.NOTIFICATION_CHANNEL_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_REMINDER)
.setTicker(taskTitle)
.setContentTitle(taskTitle)
.setContentText(appName)
.setGroup(GROUP_KEY)
.setContentText(context.getString(R.string.app_name))
.setColor(checkBoxes.getPriorityColor(task.getImportance()))
.setSmallIcon(R.drawable.ic_check_white_24dp)
.setWhen(when)
.setShowWhen(true)
.setPriority(NotificationCompat.PRIORITY_HIGH);
.setOnlyAlertOnce(false)
.setShowWhen(true);
if (preLollipop()) {
builder.setTicker(taskTitle);
}
if (atLeastJellybean()) {
builder.setContentIntent(PendingIntent.getActivity(context, (int) id, TaskIntents.getEditTaskIntent(context, null, id), PendingIntent.FLAG_UPDATE_CURRENT));

@ -6,9 +6,9 @@ import android.content.res.Resources;
import android.media.RingtoneManager;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import com.google.common.base.Strings;
import com.todoroo.astrid.activity.BeastModePreferences;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.SortHelper;
@ -27,7 +27,7 @@ import javax.inject.Inject;
import timber.log.Timber;
import static android.content.SharedPreferences.Editor;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastNougat;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybean;
public class Preferences {
@ -86,10 +86,6 @@ public class Preferences {
return time;
}
public boolean isLEDNotificationEnabled() {
return getBoolean(R.string.p_led_notification, true);
}
public boolean quietHoursEnabled() {
return getBoolean(R.string.p_rmd_enable_quiet, false);
}
@ -407,8 +403,15 @@ public class Preferences {
return directory;
}
public boolean isVibrationEnabled() {
return getBoolean(R.string.p_rmd_vibrate, true);
public int getNotificationDefaults() {
int result = 0;
if (getBoolean(R.string.p_rmd_vibrate, true)) {
result |= NotificationCompat.DEFAULT_VIBRATE;
}
if (getBoolean(R.string.p_led_notification, true)) {
result |= NotificationCompat.DEFAULT_LIGHTS;
}
return result;
}
public void remove(int resId) {
@ -418,6 +421,10 @@ public class Preferences {
}
public boolean bundleNotifications() {
return getBoolean(R.string.p_bundle_notifications, true);
return atLeastJellybean() && getBoolean(R.string.p_bundle_notifications, true);
}
public boolean usePersistentReminders() {
return getBoolean(R.string.p_rmd_persistent, true);
}
}

@ -15,6 +15,9 @@ import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.notifications.NotificationManager;
import org.tasks.time.DateTime;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
public class SnoozeActivity extends InjectingAppCompatActivity implements SnoozeCallback, DialogInterface.OnCancelListener {
@ -24,12 +27,13 @@ public class SnoozeActivity extends InjectingAppCompatActivity implements Snooze
private static final int REQUEST_DATE_TIME = 10101;
public static final String EXTRA_TASK_ID = "id";
public static final String EXTRA_TASK_IDS = "ids";
public static final String EXTRA_SNOOZE_TIME = "snooze_time";
@Inject NotificationManager notificationManager;
@Inject TaskDao taskDao;
private long taskId;
private List<Long> taskIds = new ArrayList<>();
private boolean pickingDateTime;
@Override
@ -52,7 +56,12 @@ public class SnoozeActivity extends InjectingAppCompatActivity implements Snooze
}
private void setup(Intent intent, Bundle savedInstanceState) {
taskId = intent.getLongExtra(EXTRA_TASK_ID, 0L);
if (intent.hasExtra(EXTRA_TASK_ID)) {
taskIds.add(intent.getLongExtra(EXTRA_TASK_ID, 0L));
} else if (intent.hasExtra(EXTRA_TASK_IDS)) {
//noinspection unchecked
taskIds.addAll((ArrayList<Long>) intent.getSerializableExtra(EXTRA_TASK_IDS));
}
if (savedInstanceState != null) {
pickingDateTime = savedInstanceState.getBoolean(EXTRA_PICKING_DATE_TIME, false);
@ -77,11 +86,13 @@ public class SnoozeActivity extends InjectingAppCompatActivity implements Snooze
@Override
public void snoozeForTime(DateTime time) {
Task task = new Task();
task.setId(taskId);
task.setReminderSnooze(time.getMillis());
taskDao.save(task);
notificationManager.cancel(taskId);
for (Long taskId : taskIds) {
Task task = new Task();
task.setId(taskId);
task.setReminderSnooze(time.getMillis());
taskDao.save(task);
}
notificationManager.cancel(taskIds);
setResult(RESULT_OK);
finish();
}

@ -478,6 +478,7 @@ File %1$s contained %2$s.\n\n
<!-- Reminder: Snooze button (remind again later) -->
<string name="rmd_NoA_snooze">Snooze</string>
<string name="snooze_all">Snooze all</string>
<!-- ============================================= reminder preferences == -->

Loading…
Cancel
Save