|
|
@ -29,228 +29,87 @@ import android.support.v4.app.NotificationCompat.*;
|
|
|
|
import org.isoron.androidbase.*;
|
|
|
|
import org.isoron.androidbase.*;
|
|
|
|
import org.isoron.uhabits.*;
|
|
|
|
import org.isoron.uhabits.*;
|
|
|
|
import org.isoron.uhabits.core.*;
|
|
|
|
import org.isoron.uhabits.core.*;
|
|
|
|
import org.isoron.uhabits.core.commands.*;
|
|
|
|
|
|
|
|
import org.isoron.uhabits.core.models.*;
|
|
|
|
import org.isoron.uhabits.core.models.*;
|
|
|
|
import org.isoron.uhabits.core.preferences.*;
|
|
|
|
import org.isoron.uhabits.core.preferences.*;
|
|
|
|
import org.isoron.uhabits.core.tasks.*;
|
|
|
|
|
|
|
|
import org.isoron.uhabits.core.ui.*;
|
|
|
|
import org.isoron.uhabits.core.ui.*;
|
|
|
|
import org.isoron.uhabits.core.utils.*;
|
|
|
|
|
|
|
|
import org.isoron.uhabits.intents.*;
|
|
|
|
import org.isoron.uhabits.intents.*;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import javax.inject.*;
|
|
|
|
import javax.inject.*;
|
|
|
|
|
|
|
|
|
|
|
|
import static android.graphics.BitmapFactory.*;
|
|
|
|
import static android.graphics.BitmapFactory.*;
|
|
|
|
import static org.isoron.uhabits.notifications.RingtoneManager.*;
|
|
|
|
import static org.isoron.uhabits.notifications.RingtoneManager.*;
|
|
|
|
|
|
|
|
|
|
|
|
@AppScope
|
|
|
|
@AppScope
|
|
|
|
public class AndroidNotificationTray
|
|
|
|
public class AndroidNotificationTray implements NotificationTray.SystemTray
|
|
|
|
implements CommandRunner.Listener, Preferences.Listener,
|
|
|
|
|
|
|
|
NotificationTray
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@NonNull
|
|
|
|
@NonNull
|
|
|
|
private final Context context;
|
|
|
|
private final Context context;
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
|
|
|
|
private final TaskRunner taskRunner;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
@NonNull
|
|
|
|
private final PendingIntentFactory pendingIntents;
|
|
|
|
private final PendingIntentFactory pendingIntents;
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
|
|
|
|
private final CommandRunner commandRunner;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
@NonNull
|
|
|
|
private final Preferences preferences;
|
|
|
|
private final Preferences preferences;
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull
|
|
|
|
|
|
|
|
private final HashMap<Habit, NotificationData> active;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
@Inject
|
|
|
|
public AndroidNotificationTray(@AppContext @NonNull Context context,
|
|
|
|
public AndroidNotificationTray(@AppContext @NonNull Context context,
|
|
|
|
@NonNull TaskRunner taskRunner,
|
|
|
|
|
|
|
|
@NonNull PendingIntentFactory pendingIntents,
|
|
|
|
@NonNull PendingIntentFactory pendingIntents,
|
|
|
|
@NonNull CommandRunner commandRunner,
|
|
|
|
|
|
|
|
@NonNull Preferences preferences)
|
|
|
|
@NonNull Preferences preferences)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
this.context = context;
|
|
|
|
this.context = context;
|
|
|
|
this.taskRunner = taskRunner;
|
|
|
|
|
|
|
|
this.pendingIntents = pendingIntents;
|
|
|
|
this.pendingIntents = pendingIntents;
|
|
|
|
this.commandRunner = commandRunner;
|
|
|
|
|
|
|
|
this.preferences = preferences;
|
|
|
|
this.preferences = preferences;
|
|
|
|
this.active = new HashMap<>();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public void cancel(@NonNull Habit habit)
|
|
|
|
public void removeNotification(int id)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int notificationId = getNotificationId(habit);
|
|
|
|
NotificationManagerCompat.from(context).cancel(id);
|
|
|
|
NotificationManagerCompat.from(context).cancel(notificationId);
|
|
|
|
|
|
|
|
active.remove(habit);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void showNotification(@NonNull Habit habit,
|
|
|
|
public void onCommandExecuted(@NonNull Command command,
|
|
|
|
int notificationId,
|
|
|
|
@Nullable Long refreshKey)
|
|
|
|
long timestamp,
|
|
|
|
{
|
|
|
|
long reminderTime)
|
|
|
|
if (command instanceof ToggleRepetitionCommand)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ToggleRepetitionCommand toggleCmd =
|
|
|
|
|
|
|
|
(ToggleRepetitionCommand) command;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Habit habit = toggleCmd.getHabit();
|
|
|
|
|
|
|
|
taskRunner.execute(() ->
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (habit.getCheckmarks().getTodayValue() !=
|
|
|
|
|
|
|
|
Checkmark.UNCHECKED) cancel(habit);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (command instanceof DeleteHabitsCommand)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
DeleteHabitsCommand deleteCommand = (DeleteHabitsCommand) command;
|
|
|
|
|
|
|
|
List<Habit> deleted = deleteCommand.getSelected();
|
|
|
|
|
|
|
|
for (Habit habit : deleted)
|
|
|
|
|
|
|
|
cancel(habit);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onNotificationsChanged()
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
reshowAll();
|
|
|
|
Action checkAction = new Action(R.drawable.ic_action_check,
|
|
|
|
}
|
|
|
|
context.getString(R.string.check),
|
|
|
|
|
|
|
|
pendingIntents.addCheckmark(habit, timestamp));
|
|
|
|
public void show(@NonNull Habit habit, long timestamp, long reminderTime)
|
|
|
|
|
|
|
|
{
|
|
|
|
Action snoozeAction = new Action(R.drawable.ic_action_snooze,
|
|
|
|
NotificationData data = new NotificationData(timestamp, reminderTime);
|
|
|
|
context.getString(R.string.snooze),
|
|
|
|
active.put(habit, data);
|
|
|
|
pendingIntents.snoozeNotification(habit));
|
|
|
|
taskRunner.execute(new ShowNotificationTask(habit, data));
|
|
|
|
|
|
|
|
}
|
|
|
|
Bitmap wearableBg =
|
|
|
|
|
|
|
|
decodeResource(context.getResources(), R.drawable.stripe);
|
|
|
|
public void startListening()
|
|
|
|
|
|
|
|
{
|
|
|
|
// Even though the set of actions is the same on the phone and
|
|
|
|
commandRunner.addListener(this);
|
|
|
|
// on the watch, Pebble requires us to add them to the
|
|
|
|
preferences.addListener(this);
|
|
|
|
// WearableExtender.
|
|
|
|
}
|
|
|
|
WearableExtender wearableExtender = new WearableExtender()
|
|
|
|
|
|
|
|
.setBackground(wearableBg)
|
|
|
|
public void stopListening()
|
|
|
|
.addAction(checkAction)
|
|
|
|
{
|
|
|
|
.addAction(snoozeAction);
|
|
|
|
commandRunner.removeListener(this);
|
|
|
|
|
|
|
|
preferences.removeListener(this);
|
|
|
|
Notification notification = new NotificationCompat.Builder(context)
|
|
|
|
}
|
|
|
|
.setSmallIcon(R.drawable.ic_notification)
|
|
|
|
|
|
|
|
.setContentTitle(habit.getName())
|
|
|
|
private int getNotificationId(Habit habit)
|
|
|
|
.setContentText(habit.getDescription())
|
|
|
|
{
|
|
|
|
.setContentIntent(pendingIntents.showHabit(habit))
|
|
|
|
Long id = habit.getId();
|
|
|
|
.setDeleteIntent(pendingIntents.dismissNotification(habit))
|
|
|
|
if (id == null) return 0;
|
|
|
|
.addAction(checkAction)
|
|
|
|
return (int) (id % Integer.MAX_VALUE);
|
|
|
|
.addAction(snoozeAction)
|
|
|
|
}
|
|
|
|
.setSound(getRingtoneUri(context))
|
|
|
|
|
|
|
|
.extend(wearableExtender)
|
|
|
|
private void reshowAll()
|
|
|
|
.setWhen(reminderTime)
|
|
|
|
{
|
|
|
|
.setShowWhen(true)
|
|
|
|
for (Habit habit : active.keySet())
|
|
|
|
.setOngoing(preferences.shouldMakeNotificationsSticky())
|
|
|
|
{
|
|
|
|
.build();
|
|
|
|
NotificationData data = active.get(habit);
|
|
|
|
|
|
|
|
taskRunner.execute(new ShowNotificationTask(habit, data));
|
|
|
|
NotificationManager notificationManager =
|
|
|
|
}
|
|
|
|
(NotificationManager) context.getSystemService(
|
|
|
|
}
|
|
|
|
Activity.NOTIFICATION_SERVICE);
|
|
|
|
|
|
|
|
|
|
|
|
class NotificationData
|
|
|
|
notificationManager.notify(notificationId, notification);
|
|
|
|
{
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|