/* * Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com> * * This file is part of Loop Habit Tracker. * * Loop Habit Tracker is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * Loop Habit Tracker is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.isoron.uhabits.notifications; import android.app.*; import android.content.*; import android.graphics.*; import android.support.annotation.*; import android.support.v4.app.*; import android.support.v4.app.NotificationCompat.*; import org.isoron.uhabits.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.intents.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.preferences.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.utils.*; import java.util.*; import javax.inject.*; import static android.graphics.BitmapFactory.*; import static org.isoron.uhabits.utils.RingtoneUtils.*; @AppScope public class NotificationTray implements CommandRunner.Listener, Preferences.Listener { @NonNull private final Context context; @NonNull private final TaskRunner taskRunner; @NonNull private final PendingIntentFactory pendingIntents; @NonNull private final CommandRunner commandRunner; @NonNull private final Preferences preferences; @NonNull private final HashMap<Habit, NotificationData> active; @Inject public NotificationTray(@AppContext @NonNull Context context, @NonNull TaskRunner taskRunner, @NonNull PendingIntentFactory pendingIntents, @NonNull CommandRunner commandRunner, @NonNull Preferences preferences) { this.context = context; this.taskRunner = taskRunner; this.pendingIntents = pendingIntents; this.commandRunner = commandRunner; this.preferences = preferences; this.active = new HashMap<>(); } public void cancel(@NonNull Habit habit) { int notificationId = getNotificationId(habit); NotificationManagerCompat.from(context).cancel(notificationId); active.remove(habit); } @Override public void onCommandExecuted(@NonNull Command command, @Nullable Long refreshKey) { if (command instanceof ToggleRepetitionCommand) { ToggleRepetitionCommand toggleCmd = (ToggleRepetitionCommand) command; Habit habit = toggleCmd.getHabit(); if (habit.getCheckmarks().getTodayValue() != Checkmark.UNCHECKED) cancel(habit); } if (command instanceof DeleteHabitsCommand) { DeleteHabitsCommand deleteCommand = (DeleteHabitsCommand) command; List<Habit> deleted = deleteCommand.getHabits(); for (Habit habit : deleted) cancel(habit); } } @Override public void onNotificationsChanged() { reshowAll(); } public void show(@NonNull Habit habit, long timestamp, long reminderTime) { NotificationData data = new NotificationData(timestamp, reminderTime); active.put(habit, data); taskRunner.execute(new ShowNotificationTask(habit, data)); } public void startListening() { commandRunner.addListener(this); preferences.addListener(this); } public void stopListening() { commandRunner.removeListener(this); preferences.removeListener(this); } private int getNotificationId(Habit habit) { Long id = habit.getId(); if (id == null) return 0; return (int) (id % Integer.MAX_VALUE); } private void reshowAll() { for (Habit habit : active.keySet()) { NotificationData data = active.get(habit); taskRunner.execute(new ShowNotificationTask(habit, data)); } } class NotificationData { public final long timestamp; public final long reminderTime; public NotificationData(long timestamp, long reminderTime) { this.timestamp = timestamp; this.reminderTime = reminderTime; } } private class ShowNotificationTask implements Task { int todayValue; private final Habit habit; private final long timestamp; private final long reminderTime; public ShowNotificationTask(Habit habit, NotificationData data) { this.habit = habit; this.timestamp = data.timestamp; this.reminderTime = data.reminderTime; } @Override public void doInBackground() { todayValue = habit.getCheckmarks().getTodayValue(); } @Override public void onPostExecute() { if (todayValue != Checkmark.UNCHECKED) return; if (!shouldShowReminderToday()) return; if (!habit.hasReminder()) return; Action checkAction = new Action(R.drawable.ic_action_check, context.getString(R.string.check), pendingIntents.addCheckmark(habit, timestamp)); Action snoozeAction = new Action(R.drawable.ic_action_snooze, context.getString(R.string.snooze), pendingIntents.snoozeNotification(habit)); Bitmap wearableBg = decodeResource(context.getResources(), R.drawable.stripe); // Even though the set of actions is the same on the phone and // on the watch, Pebble requires us to add them to the // WearableExtender. WearableExtender wearableExtender = new WearableExtender() .setBackground(wearableBg) .addAction(checkAction) .addAction(snoozeAction); Notification notification = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(habit.getName()) .setContentText(habit.getDescription()) .setContentIntent(pendingIntents.showHabit(habit)) .setDeleteIntent(pendingIntents.dismissNotification(habit)) .addAction(checkAction) .addAction(snoozeAction) .setSound(getRingtoneUri(context)) .extend(wearableExtender) .setWhen(reminderTime) .setShowWhen(true) .setOngoing(preferences.shouldMakeNotificationsSticky()) .build(); NotificationManager notificationManager = (NotificationManager) context.getSystemService( Activity.NOTIFICATION_SERVICE); int notificationId = getNotificationId(habit); notificationManager.notify(notificationId, notification); } private boolean shouldShowReminderToday() { if (!habit.hasReminder()) return false; Reminder reminder = habit.getReminder(); boolean reminderDays[] = reminder.getDays().toArray(); int weekday = DateUtils.getWeekday(timestamp); return reminderDays[weekday]; } } }