Convert NotificationManager to Kotlin

pull/1055/head
Alex Baker 4 years ago
parent 00bfd053c6
commit 8a30fde2f2

@ -93,11 +93,14 @@ class Notifier @Inject constructor(
&& !ringNonstop
&& !audioManager.notificationsMuted()
&& telephonyManager.callStateIdle()) {
for (notification in notifications) {
AndroidUtilities.sleepDeep(2000)
voiceOutputAssistant.speak(
notificationManager.getTaskNotification(notification).build().tickerText.toString())
}
notifications
.mapNotNull {
notificationManager.getTaskNotification(it)?.build()?.tickerText?.toString()
}
.forEach {
AndroidUtilities.sleepDeep(2000)
voiceOutputAssistant.speak(it)
}
}
}
}

@ -1,441 +0,0 @@
package org.tasks.notifications;
import static androidx.core.app.NotificationCompat.FLAG_INSISTENT;
import static androidx.core.app.NotificationCompat.FLAG_NO_CLEAR;
import static com.google.common.collect.Iterables.concat;
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.atLeastNougat;
import static com.todoroo.andlib.utility.AndroidUtilities.preOreo;
import static com.todoroo.astrid.reminders.ReminderService.TYPE_GEOFENCE_ENTER;
import static com.todoroo.astrid.reminders.ReminderService.TYPE_GEOFENCE_EXIT;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.tasks.Strings.isNullOrEmpty;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.google.common.base.Joiner;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDaoBlocking;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.reminders.ReminderService;
import dagger.hilt.android.qualifiers.ApplicationContext;
import io.reactivex.Completable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.data.LocationDaoBlocking;
import org.tasks.data.Place;
import org.tasks.intents.TaskIntents;
import org.tasks.preferences.Preferences;
import org.tasks.receivers.CompleteTaskReceiver;
import org.tasks.reminders.NotificationActivity;
import org.tasks.reminders.SnoozeActivity;
import org.tasks.reminders.SnoozeDialog;
import org.tasks.reminders.SnoozeOption;
import org.tasks.themes.ColorProvider;
import org.tasks.time.DateTime;
import timber.log.Timber;
@Singleton
public class NotificationManager {
public static final String NOTIFICATION_CHANNEL_DEFAULT = "notifications";
public static final String NOTIFICATION_CHANNEL_TASKER = "notifications_tasker";
public static final String NOTIFICATION_CHANNEL_TIMERS = "notifications_timers";
public static final String NOTIFICATION_CHANNEL_MISCELLANEOUS = "notifications_miscellaneous";
public static final int MAX_NOTIFICATIONS = 21;
static final String EXTRA_NOTIFICATION_ID = "extra_notification_id";
static final int SUMMARY_NOTIFICATION_ID = 0;
private static final String GROUP_KEY = "tasks";
private static final int NOTIFICATIONS_PER_SECOND = 4;
private final NotificationManagerCompat notificationManagerCompat;
private final ColorProvider colorProvider;
private final LocalBroadcastManager localBroadcastManager;
private final LocationDaoBlocking locationDao;
private final NotificationDaoBlocking notificationDao;
private final TaskDaoBlocking taskDao;
private final Context context;
private final Preferences preferences;
private final Throttle throttle = new Throttle(NOTIFICATIONS_PER_SECOND);
private final NotificationLimiter queue = new NotificationLimiter(MAX_NOTIFICATIONS);
@Inject
public NotificationManager(
@ApplicationContext Context context,
Preferences preferences,
NotificationDaoBlocking notificationDao,
TaskDaoBlocking taskDao,
LocationDaoBlocking locationDao,
LocalBroadcastManager localBroadcastManager) {
this.context = context;
this.preferences = preferences;
this.notificationDao = notificationDao;
this.taskDao = taskDao;
this.locationDao = locationDao;
this.localBroadcastManager = localBroadcastManager;
this.colorProvider = new ColorProvider(context, preferences);
notificationManagerCompat = NotificationManagerCompat.from(context);
}
@SuppressLint("CheckResult")
public void cancel(long id) {
if (id == SUMMARY_NOTIFICATION_ID) {
//noinspection ResultOfMethodCallIgnored
Single.fromCallable(() -> concat(notificationDao.getAll(), singletonList(id)))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::cancel);
} else {
cancel(singletonList(id));
}
}
@SuppressLint("CheckResult")
public void cancel(Iterable<Long> ids) {
for (Long id : ids) {
notificationManagerCompat.cancel(id.intValue());
queue.remove(id);
}
//noinspection ResultOfMethodCallIgnored
Completable.fromAction(() -> notificationDao.deleteAll(newArrayList(ids)))
.subscribeOn(Schedulers.io())
.subscribe(() -> notifyTasks(emptyList(), false, false, false));
}
public void restoreNotifications(boolean cancelExisting) {
List<Notification> notifications = notificationDao.getAllOrdered();
if (cancelExisting) {
for (Notification notification : notifications) {
notificationManagerCompat.cancel((int) notification.getTaskId());
}
}
if (preferences.bundleNotifications() && notifications.size() > 1) {
updateSummary(false, false, false, Collections.emptyList());
createNotifications(notifications, false, false, false, true);
} else {
createNotifications(notifications, false, false, false, false);
cancelSummaryNotification();
}
}
public void notifyTasks(
List<Notification> newNotifications, boolean alert, boolean nonstop, boolean fiveTimes) {
List<Notification> existingNotifications = notificationDao.getAllOrdered();
notificationDao.insertAll(newNotifications);
int totalCount = existingNotifications.size() + newNotifications.size();
if (totalCount == 0) {
cancelSummaryNotification();
} else if (totalCount == 1) {
List<Notification> notifications =
newArrayList(concat(existingNotifications, newNotifications));
createNotifications(notifications, alert, nonstop, fiveTimes, false);
cancelSummaryNotification();
} else if (preferences.bundleNotifications()) {
updateSummary(false, false, false, Collections.emptyList());
if (existingNotifications.size() == 1) {
createNotifications(existingNotifications, false, false, false, true);
}
if (atLeastNougat() && newNotifications.size() == 1) {
createNotifications(newNotifications, alert, nonstop, fiveTimes, true);
} else {
createNotifications(newNotifications, false, false, false, true);
updateSummary(alert, nonstop, fiveTimes, newNotifications);
}
} else {
createNotifications(newNotifications, alert, nonstop, fiveTimes, false);
}
localBroadcastManager.broadcastRefresh();
}
private void createNotifications(
List<Notification> notifications,
boolean alert,
boolean nonstop,
boolean fiveTimes,
boolean useGroupKey) {
for (Notification notification : notifications) {
NotificationCompat.Builder builder = getTaskNotification(notification);
if (builder == null) {
notificationManagerCompat.cancel((int) notification.getTaskId());
notificationDao.delete(notification.getTaskId());
} else {
builder
.setGroup(
useGroupKey
? GROUP_KEY
: (atLeastNougat() ? Long.toString(notification.getTaskId()) : null))
.setGroupAlertBehavior(
alert
? NotificationCompat.GROUP_ALERT_CHILDREN
: NotificationCompat.GROUP_ALERT_SUMMARY);
notify(notification.getTaskId(), builder, alert, nonstop, fiveTimes);
alert = false;
}
}
}
public void notify(
long notificationId,
NotificationCompat.Builder builder,
boolean alert,
boolean nonstop,
boolean fiveTimes) {
if (!preferences.getBoolean(R.string.p_rmd_enabled, true)) {
return;
}
builder.setLocalOnly(!preferences.getBoolean(R.string.p_wearable_notifications, true));
if (preOreo()) {
if (alert) {
builder
.setSound(preferences.getRingtone())
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(preferences.getNotificationDefaults());
} else {
builder.setDefaults(0).setTicker(null);
}
}
android.app.Notification notification = builder.build();
int ringTimes = fiveTimes ? 5 : 1;
if (alert && nonstop) {
notification.flags |= FLAG_INSISTENT;
ringTimes = 1;
}
if (preferences.usePersistentReminders()) {
notification.flags |= FLAG_NO_CLEAR;
}
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);
List<Long> evicted = queue.add(notificationId);
if (evicted.size() > 0) {
cancel(evicted);
}
for (int i = 0; i < ringTimes; i++) {
throttle.run(() -> notificationManagerCompat.notify((int) notificationId, notification));
}
}
private void updateSummary(
boolean notify, boolean nonStop, boolean fiveTimes, List<Notification> newNotifications) {
List<Task> tasks = taskDao.activeNotifications();
int taskCount = tasks.size();
if (taskCount == 0) {
cancelSummaryNotification();
return;
}
ArrayList<Long> taskIds = new ArrayList<>(transform(tasks, Task::getId));
Filter filter =
new Filter(
context.getString(R.string.notifications),
new QueryTemplate()
.join(Join.inner(Notification.TABLE, Task.ID.eq(Notification.TASK))));
long when = notificationDao.latestTimestamp();
int maxPriority = 3;
String summaryTitle =
context.getResources().getQuantityString(R.plurals.task_count, taskCount, taskCount);
NotificationCompat.InboxStyle style =
new NotificationCompat.InboxStyle().setBigContentTitle(summaryTitle);
List<String> titles = new ArrayList<>();
List<String> ticker = new ArrayList<>();
for (Task task : tasks) {
String title = task.getTitle();
style.addLine(title);
titles.add(title);
maxPriority = Math.min(maxPriority, task.getPriority());
}
for (Notification notification : newNotifications) {
Task task = tryFind(tasks, t -> t.getId() == notification.getTaskId()).orNull();
if (task == null) {
continue;
}
ticker.add(task.getTitle());
}
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, NotificationManager.NOTIFICATION_CHANNEL_DEFAULT)
.setContentTitle(summaryTitle)
.setContentText(
Joiner.on(context.getString(R.string.list_separator_with_space)).join(titles))
.setShowWhen(true)
.setWhen(when)
.setSmallIcon(R.drawable.ic_done_all_white_24dp)
.setStyle(style)
.setColor(colorProvider.getPriorityColor(maxPriority, true))
.setOnlyAlertOnce(false)
.setContentIntent(
PendingIntent.getActivity(
context,
0,
TaskIntents.getTaskListIntent(context, filter),
PendingIntent.FLAG_UPDATE_CURRENT))
.setGroupSummary(true)
.setGroup(GROUP_KEY)
.setTicker(
Joiner.on(context.getString(R.string.list_separator_with_space)).join(ticker))
.setGroupAlertBehavior(
notify
? NotificationCompat.GROUP_ALERT_SUMMARY
: NotificationCompat.GROUP_ALERT_CHILDREN);
Intent snoozeIntent = SnoozeActivity.newIntent(context, taskIds);
builder.addAction(
R.drawable.ic_snooze_white_24dp,
context.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(Notification notification) {
long id = notification.getTaskId();
int type = notification.getType();
long when = notification.getTimestamp();
Task task = taskDao.fetchBlocking(id);
if (task == null) {
Timber.e("Could not find %s", id);
return null;
}
// you're done, or not yours - don't sound, do delete
if (task.isCompleted() || task.isDeleted()) {
return null;
}
// new task edit in progress
if (isNullOrEmpty(task.getTitle())) {
return null;
}
// it's hidden - don't sound, don't delete
if (task.isHidden() && type == ReminderService.TYPE_RANDOM) {
return null;
}
// task due date was changed, but alarm wasn't rescheduled
boolean dueInFuture =
task.hasDueTime()
&& new DateTime(task.getDueDate()).startOfMinute().getMillis() > DateUtilities.now()
|| !task.hasDueTime()
&& task.getDueDate() - DateUtilities.now() > DateUtilities.ONE_DAY;
if ((type == ReminderService.TYPE_DUE || type == ReminderService.TYPE_OVERDUE)
&& (!task.hasDueDate() || dueInFuture)) {
return null;
}
// read properties
final String taskTitle = task.getTitle();
final String taskDescription = task.getNotes();
// update last reminder time
long reminderTime = new DateTime(when).endOfMinute().getMillis();
if (reminderTime != task.getReminderLast()) {
task.setReminderLast(reminderTime);
taskDao.save(task);
}
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, NotificationManager.NOTIFICATION_CHANNEL_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_REMINDER)
.setContentTitle(taskTitle)
.setColor(colorProvider.getPriorityColor(task.getPriority(), true))
.setSmallIcon(R.drawable.ic_check_white_24dp)
.setWhen(when)
.setOnlyAlertOnce(false)
.setShowWhen(true)
.setTicker(taskTitle);
Intent intent = NotificationActivity.newIntent(context, taskTitle, id);
builder.setContentIntent(
PendingIntent.getActivity(context, (int) id, intent, PendingIntent.FLAG_UPDATE_CURRENT));
if (type == TYPE_GEOFENCE_ENTER || type == TYPE_GEOFENCE_EXIT) {
Place place = locationDao.getPlace(notification.getLocation());
if (place != null) {
builder.setContentText(
context.getString(
type == TYPE_GEOFENCE_ENTER
? R.string.location_arrived
: R.string.location_departed,
place.getDisplayName()));
}
} else if (!isNullOrEmpty(taskDescription)) {
builder
.setContentText(taskDescription)
.setStyle(new NotificationCompat.BigTextStyle().bigText(taskDescription));
}
Intent completeIntent = new Intent(context, CompleteTaskReceiver.class);
completeIntent.putExtra(CompleteTaskReceiver.TASK_ID, id);
PendingIntent completePendingIntent =
PendingIntent.getBroadcast(
context, (int) id, completeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action completeAction =
new NotificationCompat.Action.Builder(
R.drawable.ic_check_white_24dp,
context.getString(R.string.rmd_NoA_done),
completePendingIntent)
.build();
Intent snoozeIntent = SnoozeActivity.newIntent(context, id);
PendingIntent snoozePendingIntent =
PendingIntent.getActivity(
context, (int) id, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.WearableExtender wearableExtender =
new NotificationCompat.WearableExtender();
wearableExtender.addAction(completeAction);
for (final SnoozeOption snoozeOption : SnoozeDialog.getSnoozeOptions(preferences)) {
final long timestamp = snoozeOption.getDateTime().getMillis();
Intent wearableIntent = SnoozeActivity.newIntent(context, id);
wearableIntent.setAction(String.format("snooze-%s-%s", id, timestamp));
wearableIntent.putExtra(SnoozeActivity.EXTRA_SNOOZE_TIME, timestamp);
PendingIntent wearablePendingIntent =
PendingIntent.getActivity(
context, (int) id, wearableIntent, PendingIntent.FLAG_UPDATE_CURRENT);
wearableExtender.addAction(
new NotificationCompat.Action.Builder(
R.drawable.ic_snooze_white_24dp,
context.getString(snoozeOption.getResId()),
wearablePendingIntent)
.build());
}
return builder
.addAction(completeAction)
.addAction(
R.drawable.ic_snooze_white_24dp,
context.getString(R.string.rmd_NoA_snooze),
snoozePendingIntent)
.extend(wearableExtender);
}
private void cancelSummaryNotification() {
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID);
}
}

@ -0,0 +1,401 @@
package org.tasks.notifications
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.dao.TaskDaoBlocking
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.reminders.ReminderService
import dagger.hilt.android.qualifiers.ApplicationContext
import io.reactivex.Completable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.LocationDaoBlocking
import org.tasks.intents.TaskIntents
import org.tasks.preferences.Preferences
import org.tasks.receivers.CompleteTaskReceiver
import org.tasks.reminders.NotificationActivity
import org.tasks.reminders.SnoozeActivity
import org.tasks.reminders.SnoozeDialog
import org.tasks.themes.ColorProvider
import org.tasks.time.DateTime
import timber.log.Timber
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.min
@Singleton
class NotificationManager @Inject constructor(
@param:ApplicationContext private val context: Context,
private val preferences: Preferences,
private val notificationDao: NotificationDaoBlocking,
private val taskDao: TaskDaoBlocking,
private val locationDao: LocationDaoBlocking,
private val localBroadcastManager: LocalBroadcastManager) {
private val notificationManagerCompat = NotificationManagerCompat.from(context)
private val colorProvider = ColorProvider(context, preferences)
private val throttle = Throttle(NOTIFICATIONS_PER_SECOND)
private val queue = NotificationLimiter(MAX_NOTIFICATIONS)
@SuppressLint("CheckResult")
fun cancel(id: Long) {
if (id == SUMMARY_NOTIFICATION_ID.toLong()) {
Single.fromCallable { notificationDao.getAll() + id }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { ids: Iterable<Long> -> this.cancel(ids) }
} else {
cancel(listOf(id))
}
}
@SuppressLint("CheckResult")
fun cancel(ids: Iterable<Long>) {
for (id in ids) {
notificationManagerCompat.cancel(id.toInt())
queue.remove(id)
}
Completable.fromAction { notificationDao.deleteAll(ids.toList()) }
.subscribeOn(Schedulers.io())
.subscribe {
notifyTasks(
emptyList(),
alert = false,
nonstop = false,
fiveTimes = false)
}
}
fun restoreNotifications(cancelExisting: Boolean) {
val notifications = notificationDao.getAllOrdered()
if (cancelExisting) {
for (notification in notifications) {
notificationManagerCompat.cancel(notification.taskId.toInt())
}
}
if (preferences.bundleNotifications() && notifications.size > 1) {
updateSummary(
notify = false,
nonStop = false,
fiveTimes = false,
newNotifications = emptyList())
createNotifications(
notifications,
alert = false,
nonstop = false,
fiveTimes = false,
useGroupKey = true)
} else {
createNotifications(
notifications,
alert = false,
nonstop = false,
fiveTimes = false,
useGroupKey = false)
cancelSummaryNotification()
}
}
fun notifyTasks(
newNotifications: List<Notification>, alert: Boolean, nonstop: Boolean, fiveTimes: Boolean) {
val existingNotifications = notificationDao.getAllOrdered()
notificationDao.insertAll(newNotifications)
val totalCount = existingNotifications.size + newNotifications.size
if (totalCount == 0) {
cancelSummaryNotification()
} else if (totalCount == 1) {
val notifications = existingNotifications + newNotifications
createNotifications(notifications, alert, nonstop, fiveTimes, false)
cancelSummaryNotification()
} else if (preferences.bundleNotifications()) {
updateSummary(
notify = false,
nonStop = false,
fiveTimes = false,
newNotifications = emptyList())
if (existingNotifications.size == 1) {
createNotifications(
existingNotifications,
alert = false,
nonstop = false,
fiveTimes = false,
useGroupKey = true)
}
if (AndroidUtilities.atLeastNougat() && newNotifications.size == 1) {
createNotifications(newNotifications, alert, nonstop, fiveTimes, true)
} else {
createNotifications(
newNotifications,
alert = false,
nonstop = false,
fiveTimes = false,
useGroupKey = true)
updateSummary(alert, nonstop, fiveTimes, newNotifications)
}
} else {
createNotifications(newNotifications, alert, nonstop, fiveTimes, false)
}
localBroadcastManager.broadcastRefresh()
}
private fun createNotifications(
notifications: List<Notification>,
alert: Boolean,
nonstop: Boolean,
fiveTimes: Boolean,
useGroupKey: Boolean) {
var alert = alert
for (notification in notifications) {
val builder = getTaskNotification(notification)
if (builder == null) {
notificationManagerCompat.cancel(notification.taskId.toInt())
notificationDao.delete(notification.taskId)
} else {
builder
.setGroup(
if (useGroupKey) GROUP_KEY else if (AndroidUtilities.atLeastNougat()) java.lang.Long.toString(notification.taskId) else null)
.setGroupAlertBehavior(
if (alert) NotificationCompat.GROUP_ALERT_CHILDREN else NotificationCompat.GROUP_ALERT_SUMMARY)
notify(notification.taskId, builder, alert, nonstop, fiveTimes)
alert = false
}
}
}
fun notify(
notificationId: Long,
builder: NotificationCompat.Builder,
alert: Boolean,
nonstop: Boolean,
fiveTimes: Boolean) {
if (!preferences.getBoolean(R.string.p_rmd_enabled, true)) {
return
}
builder.setLocalOnly(!preferences.getBoolean(R.string.p_wearable_notifications, true))
if (AndroidUtilities.preOreo()) {
if (alert) {
builder
.setSound(preferences.ringtone)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(preferences.notificationDefaults)
} else {
builder.setDefaults(0).setTicker(null)
}
}
val notification = builder.build()
var ringTimes = if (fiveTimes) 5 else 1
if (alert && nonstop) {
notification.flags = notification.flags or NotificationCompat.FLAG_INSISTENT
ringTimes = 1
}
if (preferences.usePersistentReminders()) {
notification.flags = notification.flags or NotificationCompat.FLAG_NO_CLEAR
}
val deleteIntent = Intent(context, NotificationClearedReceiver::class.java)
deleteIntent.putExtra(EXTRA_NOTIFICATION_ID, notificationId)
notification.deleteIntent = PendingIntent.getBroadcast(
context, notificationId.toInt(), deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val evicted = queue.add(notificationId)
if (evicted.size > 0) {
cancel(evicted)
}
for (i in 0 until ringTimes) {
throttle.run { notificationManagerCompat.notify(notificationId.toInt(), notification) }
}
}
private fun updateSummary(
notify: Boolean, nonStop: Boolean, fiveTimes: Boolean, newNotifications: List<Notification>) {
val tasks = taskDao.activeNotifications()
val taskCount = tasks.size
if (taskCount == 0) {
cancelSummaryNotification()
return
}
val taskIds = tasks.map { it.id }
val filter = Filter(
context.getString(R.string.notifications),
QueryTemplate()
.join(inner(Notification.TABLE, Task.ID.eq(Notification.TASK))))
val `when` = notificationDao.latestTimestamp()
var maxPriority = 3
val summaryTitle = context.resources.getQuantityString(R.plurals.task_count, taskCount, taskCount)
val style = NotificationCompat.InboxStyle().setBigContentTitle(summaryTitle)
val titles: MutableList<String?> = ArrayList()
val ticker: MutableList<String?> = ArrayList()
for (task in tasks) {
val title = task.title
style.addLine(title)
titles.add(title)
maxPriority = min(maxPriority, task.priority)
}
for (notification in newNotifications) {
tasks.find { it.id == notification.taskId }?.let { ticker.add(it.title) }
}
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_DEFAULT)
.setContentTitle(summaryTitle)
.setContentText(
titles.joinToString(context.getString(R.string.list_separator_with_space)))
.setShowWhen(true)
.setWhen(`when`)
.setSmallIcon(R.drawable.ic_done_all_white_24dp)
.setStyle(style)
.setColor(colorProvider.getPriorityColor(maxPriority, true))
.setOnlyAlertOnce(false)
.setContentIntent(
PendingIntent.getActivity(
context,
0,
TaskIntents.getTaskListIntent(context, filter),
PendingIntent.FLAG_UPDATE_CURRENT))
.setGroupSummary(true)
.setGroup(GROUP_KEY)
.setTicker(
ticker.joinToString(context.getString(R.string.list_separator_with_space)))
.setGroupAlertBehavior(
if (notify) NotificationCompat.GROUP_ALERT_SUMMARY else NotificationCompat.GROUP_ALERT_CHILDREN)
val snoozeIntent = SnoozeActivity.newIntent(context, taskIds)
builder.addAction(
R.drawable.ic_snooze_white_24dp,
context.getString(R.string.snooze_all),
PendingIntent.getActivity(context, 0, snoozeIntent, PendingIntent.FLAG_CANCEL_CURRENT))
notify(SUMMARY_NOTIFICATION_ID.toLong(), builder, notify, nonStop, fiveTimes)
}
fun getTaskNotification(notification: Notification): NotificationCompat.Builder? {
val id = notification.taskId
val type = notification.type
val `when` = notification.timestamp
val task = taskDao.fetchBlocking(id)
if (task == null) {
Timber.e("Could not find %s", id)
return null
}
// you're done, or not yours - don't sound, do delete
if (task.isCompleted || task.isDeleted) {
return null
}
// new task edit in progress
if (isNullOrEmpty(task.title)) {
return null
}
// it's hidden - don't sound, don't delete
if (task.isHidden && type == ReminderService.TYPE_RANDOM) {
return null
}
// task due date was changed, but alarm wasn't rescheduled
val dueInFuture = (task.hasDueTime()
&& DateTime(task.dueDate).startOfMinute().millis > DateUtilities.now()
|| !task.hasDueTime()
&& task.dueDate - DateUtilities.now() > DateUtilities.ONE_DAY)
if ((type == ReminderService.TYPE_DUE || type == ReminderService.TYPE_OVERDUE)
&& (!task.hasDueDate() || dueInFuture)) {
return null
}
// read properties
val taskTitle = task.title
val taskDescription = task.notes
// update last reminder time
val reminderTime = DateTime(`when`).endOfMinute().millis
if (reminderTime != task.reminderLast) {
task.reminderLast = reminderTime
taskDao.save(task)
}
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_REMINDER)
.setContentTitle(taskTitle)
.setColor(colorProvider.getPriorityColor(task.priority, true))
.setSmallIcon(R.drawable.ic_check_white_24dp)
.setWhen(`when`)
.setOnlyAlertOnce(false)
.setShowWhen(true)
.setTicker(taskTitle)
val intent = NotificationActivity.newIntent(context, taskTitle, id)
builder.setContentIntent(
PendingIntent.getActivity(context, id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT))
if (type == ReminderService.TYPE_GEOFENCE_ENTER || type == ReminderService.TYPE_GEOFENCE_EXIT) {
val place = locationDao.getPlace(notification.location!!)
if (place != null) {
builder.setContentText(
context.getString(
if (type == ReminderService.TYPE_GEOFENCE_ENTER) R.string.location_arrived else R.string.location_departed,
place.displayName))
}
} else if (!isNullOrEmpty(taskDescription)) {
builder
.setContentText(taskDescription)
.setStyle(NotificationCompat.BigTextStyle().bigText(taskDescription))
}
val completeIntent = Intent(context, CompleteTaskReceiver::class.java)
completeIntent.putExtra(CompleteTaskReceiver.TASK_ID, id)
val completePendingIntent = PendingIntent.getBroadcast(
context, id.toInt(), completeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val completeAction = NotificationCompat.Action.Builder(
R.drawable.ic_check_white_24dp,
context.getString(R.string.rmd_NoA_done),
completePendingIntent)
.build()
val snoozeIntent = SnoozeActivity.newIntent(context, id)
val snoozePendingIntent = PendingIntent.getActivity(
context, id.toInt(), snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val wearableExtender = NotificationCompat.WearableExtender()
wearableExtender.addAction(completeAction)
for (snoozeOption in SnoozeDialog.getSnoozeOptions(preferences)) {
val timestamp = snoozeOption.dateTime.millis
val wearableIntent = SnoozeActivity.newIntent(context, id)
wearableIntent.action = String.format("snooze-%s-%s", id, timestamp)
wearableIntent.putExtra(SnoozeActivity.EXTRA_SNOOZE_TIME, timestamp)
val wearablePendingIntent = PendingIntent.getActivity(
context, id.toInt(), wearableIntent, PendingIntent.FLAG_UPDATE_CURRENT)
wearableExtender.addAction(
NotificationCompat.Action.Builder(
R.drawable.ic_snooze_white_24dp,
context.getString(snoozeOption.resId),
wearablePendingIntent)
.build())
}
return builder
.addAction(completeAction)
.addAction(
R.drawable.ic_snooze_white_24dp,
context.getString(R.string.rmd_NoA_snooze),
snoozePendingIntent)
.extend(wearableExtender)
}
private fun cancelSummaryNotification() {
notificationManagerCompat.cancel(SUMMARY_NOTIFICATION_ID)
}
companion object {
const val NOTIFICATION_CHANNEL_DEFAULT = "notifications"
const val NOTIFICATION_CHANNEL_TASKER = "notifications_tasker"
const val NOTIFICATION_CHANNEL_TIMERS = "notifications_timers"
const val NOTIFICATION_CHANNEL_MISCELLANEOUS = "notifications_miscellaneous"
const val MAX_NOTIFICATIONS = 21
const val EXTRA_NOTIFICATION_ID = "extra_notification_id"
const val SUMMARY_NOTIFICATION_ID = 0
private const val GROUP_KEY = "tasks"
private const val NOTIFICATIONS_PER_SECOND = 4
}
}

@ -43,10 +43,10 @@ public class SnoozeActivity extends InjectingAppCompatActivity
return intent;
}
public static Intent newIntent(Context context, ArrayList<Long> ids) {
public static Intent newIntent(Context context, List<Long> ids) {
Intent intent = new Intent(context, SnoozeActivity.class);
intent.setFlags(FLAGS);
intent.putExtra(SnoozeActivity.EXTRA_TASK_IDS, ids);
intent.putExtra(SnoozeActivity.EXTRA_TASK_IDS, new ArrayList(ids));
return intent;
}

Loading…
Cancel
Save