From 77f406dceed91b884a919b7b6cb72fe91b3adabd Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Sun, 17 Jul 2016 17:28:14 -0400 Subject: [PATCH] Refactor broadcast receivers and add a public receiver Fixes #6 --- .../PebbleReceiverTest.java | 1 + app/src/main/AndroidManifest.xml | 41 ++++-- .../org/isoron/uhabits/BaseComponent.java | 8 +- .../isoron/uhabits/HabitPendingIntents.java | 20 +-- .../uhabits/receivers/IntentParser.java | 79 +++++++++++ .../{pebble => receivers}/PebbleReceiver.java | 2 +- .../uhabits/receivers/ReceiverActions.java | 62 +++++++++ .../ReminderReceiver.java} | 129 +++++++----------- .../uhabits/receivers/WidgetReceiver.java | 115 ++++++++++++++++ .../isoron/uhabits/utils/ReminderUtils.java | 5 +- 10 files changed, 356 insertions(+), 106 deletions(-) rename app/src/androidTest/java/org/isoron/uhabits/{pebble => receivers}/PebbleReceiverTest.java (99%) create mode 100644 app/src/main/java/org/isoron/uhabits/receivers/IntentParser.java rename app/src/main/java/org/isoron/uhabits/{pebble => receivers}/PebbleReceiver.java (99%) create mode 100644 app/src/main/java/org/isoron/uhabits/receivers/ReceiverActions.java rename app/src/main/java/org/isoron/uhabits/{HabitBroadcastReceiver.java => receivers/ReminderReceiver.java} (71%) create mode 100644 app/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java diff --git a/app/src/androidTest/java/org/isoron/uhabits/pebble/PebbleReceiverTest.java b/app/src/androidTest/java/org/isoron/uhabits/receivers/PebbleReceiverTest.java similarity index 99% rename from app/src/androidTest/java/org/isoron/uhabits/pebble/PebbleReceiverTest.java rename to app/src/androidTest/java/org/isoron/uhabits/receivers/PebbleReceiverTest.java index c79c4a7f9..631f2d396 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/pebble/PebbleReceiverTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/receivers/PebbleReceiverTest.java @@ -29,6 +29,7 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; +import org.isoron.uhabits.receivers.*; import org.json.*; import org.junit.*; import org.junit.runner.*; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index df468bb5e..a72f836da 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,13 +28,13 @@ + android:maxSdkVersion="18"/> + android:maxSdkVersion="18"/> - + + android:supportsRtl="true" + android:theme="@style/AppBaseTheme"> + android:value=".MainActivity"/> - + + + + + + + + + + + + + + + + + + + - + + + - + - + - diff --git a/app/src/main/java/org/isoron/uhabits/BaseComponent.java b/app/src/main/java/org/isoron/uhabits/BaseComponent.java index 4a2e74d51..48b394fed 100644 --- a/app/src/main/java/org/isoron/uhabits/BaseComponent.java +++ b/app/src/main/java/org/isoron/uhabits/BaseComponent.java @@ -22,7 +22,7 @@ import org.isoron.uhabits.commands.*; import org.isoron.uhabits.io.*; import org.isoron.uhabits.models.*; -import org.isoron.uhabits.pebble.*; +import org.isoron.uhabits.receivers.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.ui.*; import org.isoron.uhabits.ui.habits.edit.*; @@ -49,7 +49,7 @@ public interface BaseComponent void inject(HabitCardListCache habitCardListCache); - void inject(HabitBroadcastReceiver habitBroadcastReceiver); + void inject(WidgetReceiver widgetReceiver); void inject(ListHabitsSelectionMenu listHabitsSelectionMenu); @@ -102,4 +102,8 @@ public interface BaseComponent void inject(PebbleReceiver receiver); void inject(HeaderView headerView); + + void inject(ReceiverActions receiverActions); + + void inject(ReminderReceiver reminderReceiver); } diff --git a/app/src/main/java/org/isoron/uhabits/HabitPendingIntents.java b/app/src/main/java/org/isoron/uhabits/HabitPendingIntents.java index b5eb5bbb5..e6bce8be5 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitPendingIntents.java +++ b/app/src/main/java/org/isoron/uhabits/HabitPendingIntents.java @@ -24,12 +24,12 @@ import android.net.*; import org.isoron.uhabits.models.*; +import org.isoron.uhabits.receivers.*; import org.isoron.uhabits.ui.habits.show.*; public abstract class HabitPendingIntents { - - private static final String BASE_URL = + public static final String BASE_URL = "content://org.isoron.uhabits/habit/"; public static PendingIntent addCheckmark(Context context, @@ -37,9 +37,9 @@ public static PendingIntent addCheckmark(Context context, Long timestamp) { Uri data = habit.getUri(); - Intent checkIntent = new Intent(context, HabitBroadcastReceiver.class); + Intent checkIntent = new Intent(context, WidgetReceiver.class); checkIntent.setData(data); - checkIntent.setAction(HabitBroadcastReceiver.ACTION_CHECK); + checkIntent.setAction(WidgetReceiver.ACTION_ADD_REPETITION); if (timestamp != null) checkIntent.putExtra("timestamp", timestamp); return PendingIntent.getBroadcast(context, 1, checkIntent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -47,8 +47,8 @@ public static PendingIntent addCheckmark(Context context, public static PendingIntent dismissNotification(Context context) { - Intent deleteIntent = new Intent(context, HabitBroadcastReceiver.class); - deleteIntent.setAction(HabitBroadcastReceiver.ACTION_DISMISS); + Intent deleteIntent = new Intent(context, ReminderReceiver.class); + deleteIntent.setAction(WidgetReceiver.ACTION_DISMISS_REMINDER); return PendingIntent.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT); } @@ -56,9 +56,9 @@ public static PendingIntent dismissNotification(Context context) public static PendingIntent snoozeNotification(Context context, Habit habit) { Uri data = habit.getUri(); - Intent snoozeIntent = new Intent(context, HabitBroadcastReceiver.class); + Intent snoozeIntent = new Intent(context, ReminderReceiver.class); snoozeIntent.setData(data); - snoozeIntent.setAction(HabitBroadcastReceiver.ACTION_SNOOZE); + snoozeIntent.setAction(ReminderReceiver.ACTION_SNOOZE_REMINDER); return PendingIntent.getBroadcast(context, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT); } @@ -68,9 +68,9 @@ public static PendingIntent toggleCheckmark(Context context, Long timestamp) { Uri data = habit.getUri(); - Intent checkIntent = new Intent(context, HabitBroadcastReceiver.class); + Intent checkIntent = new Intent(context, WidgetReceiver.class); checkIntent.setData(data); - checkIntent.setAction(HabitBroadcastReceiver.ACTION_TOGGLE); + checkIntent.setAction(WidgetReceiver.ACTION_TOGGLE_REPETITION); if (timestamp != null) checkIntent.putExtra("timestamp", timestamp); return PendingIntent.getBroadcast(context, 2, checkIntent, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/app/src/main/java/org/isoron/uhabits/receivers/IntentParser.java b/app/src/main/java/org/isoron/uhabits/receivers/IntentParser.java new file mode 100644 index 000000000..ac8013fcc --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/receivers/IntentParser.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.receivers; + +import android.content.*; +import android.net.*; +import android.support.annotation.*; + +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; + +public class IntentParser +{ + private HabitList habits; + + public IntentParser(@NonNull HabitList habits) + { + this.habits = habits; + } + + public CheckmarkIntentData parseCheckmarkIntent(@NonNull Intent intent) + { + Uri uri = intent.getData(); + CheckmarkIntentData data = new CheckmarkIntentData(); + data.habit = parseHabit(uri); + data.timestamp = parseTimestamp(intent); + return data; + } + + @NonNull + protected Habit parseHabit(@NonNull Uri uri) + { + long habitId = ContentUris.parseId(uri); + + Habit habit = habits.getById(habitId); + if (habit == null) + throw new IllegalArgumentException("habit not found"); + + return habit; + } + + @NonNull + protected Long parseTimestamp(@NonNull Intent intent) + { + long today = DateUtils.getStartOfToday(); + + Long timestamp = intent.getLongExtra("timestamp", today); + timestamp = DateUtils.getStartOfDay(timestamp); + + if (timestamp < 0 || timestamp > today) + throw new IllegalArgumentException("timestamp is not valid"); + + return timestamp; + } + + class CheckmarkIntentData + { + public Habit habit; + + public Long timestamp; + } +} diff --git a/app/src/main/java/org/isoron/uhabits/pebble/PebbleReceiver.java b/app/src/main/java/org/isoron/uhabits/receivers/PebbleReceiver.java similarity index 99% rename from app/src/main/java/org/isoron/uhabits/pebble/PebbleReceiver.java rename to app/src/main/java/org/isoron/uhabits/receivers/PebbleReceiver.java index 9159d11e2..3f9cf27c2 100644 --- a/app/src/main/java/org/isoron/uhabits/pebble/PebbleReceiver.java +++ b/app/src/main/java/org/isoron/uhabits/receivers/PebbleReceiver.java @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.pebble; +package org.isoron.uhabits.receivers; import android.content.*; import android.support.annotation.*; diff --git a/app/src/main/java/org/isoron/uhabits/receivers/ReceiverActions.java b/app/src/main/java/org/isoron/uhabits/receivers/ReceiverActions.java new file mode 100644 index 000000000..14d25c902 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/receivers/ReceiverActions.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.receivers; + +import android.support.annotation.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.commands.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.tasks.*; + +import javax.inject.*; + +public class ReceiverActions +{ + @Inject + CommandRunner commandRunner; + + public ReceiverActions() + { + HabitsApplication.getComponent().inject(this); + } + + public void add_repetition(@NonNull Habit habit, long timestamp) + { + Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); + if (rep != null) return; + toggle_repetition(habit, timestamp); + } + + public void remove_repetition(@NonNull Habit habit, long timestamp) + { + Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); + if (rep == null) return; + toggle_repetition(habit, timestamp); + } + + public void toggle_repetition(@NonNull Habit habit, long timestamp) + { + new SimpleTask(() -> { + commandRunner.execute(new ToggleRepetitionCommand(habit, timestamp), + habit.getId()); + }).execute(); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java b/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java similarity index 71% rename from app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java rename to app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index 0b841a1a6..dec2f6a21 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java +++ b/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits; +package org.isoron.uhabits.receivers; import android.app.*; import android.content.*; @@ -26,8 +26,9 @@ import android.os.*; import android.preference.*; import android.support.v4.app.*; +import android.util.*; -import org.isoron.uhabits.commands.*; +import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.utils.*; @@ -41,29 +42,23 @@ *

* All broadcast messages are received and processed by this class. */ -public class HabitBroadcastReceiver extends BroadcastReceiver +public class ReminderReceiver extends BroadcastReceiver { - public static final String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK"; - - public static final String ACTION_DISMISS = - "org.isoron.uhabits.ACTION_DISMISS"; + public static final String ACTION_DISMISS_REMINDER = + "org.isoron.uhabits.ACTION_DISMISS_REMINDER"; public static final String ACTION_SHOW_REMINDER = "org.isoron.uhabits.ACTION_SHOW_REMINDER"; - public static final String ACTION_SNOOZE = - "org.isoron.uhabits.ACTION_SNOOZE"; + public static final String ACTION_SNOOZE_REMINDER = + "org.isoron.uhabits.ACTION_SNOOZE_REMINDER"; - public static final String ACTION_TOGGLE = - "org.isoron.uhabits.ACTION_TOGGLE"; + private static final String TAG = "ReminderReceiver"; @Inject HabitList habits; - @Inject - CommandRunner commandRunner; - - public HabitBroadcastReceiver() + public ReminderReceiver() { super(); HabitsApplication.getComponent().inject(this); @@ -72,79 +67,44 @@ public HabitBroadcastReceiver() @Override public void onReceive(final Context context, Intent intent) { - switch (intent.getAction()) - { - case ACTION_SHOW_REMINDER: - createNotification(context, intent); - createReminderAlarmsDelayed(context); - break; - - case ACTION_DISMISS: - // NOP - break; - - case ACTION_CHECK: - addRepetition(context, intent); - break; - - case ACTION_TOGGLE: - toggleRepetition(context, intent); - break; - - case ACTION_SNOOZE: - snoozeHabit(context, intent); - break; - - case Intent.ACTION_BOOT_COMPLETED: - ReminderUtils.createReminderAlarms(context, habits); - break; - } - } - - private void addOrRemoveRepetition(Context context, - Intent intent, - boolean abortIfExists) - { - Uri data = intent.getData(); - long today = DateUtils.getStartOfToday(); - Long timestamp = intent.getLongExtra("timestamp", today); + Log.i(TAG, String.format("Received intent: %s", intent.toString())); - long habitId = ContentUris.parseId(data); - Habit habit = habits.getById(habitId); try { - if (habit == null) return; + switch (intent.getAction()) + { + case ACTION_SHOW_REMINDER: + onActionShowReminder(context, intent); + break; - Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); - if (abortIfExists && rep != null) return; + case ACTION_DISMISS_REMINDER: + // NOP + break; - commandRunner.execute(new ToggleRepetitionCommand(habit, timestamp), - habitId); + case ACTION_SNOOZE_REMINDER: + onActionSnoozeReminder(context, intent); + break; + + case Intent.ACTION_BOOT_COMPLETED: + onActionBootCompleted(context); + break; + } } - finally + catch (RuntimeException e) { - dismissNotification(context, habitId); + Log.e(TAG, "could not process intent", e); } } - private void addRepetition(Context context, Intent intent) + protected void onActionBootCompleted(Context context) { - addOrRemoveRepetition(context, intent, true); + ReminderUtils.createReminderAlarms(context, habits); } - private boolean checkWeekday(Intent intent, Habit habit) + protected void onActionShowReminder(Context context, Intent intent) { - if (!habit.hasReminder()) return false; - Reminder reminder = habit.getReminder(); - - Long timestamp = - intent.getLongExtra("timestamp", DateUtils.getStartOfToday()); - - boolean reminderDays[] = - DateUtils.unpackWeekdayList(reminder.getDays()); - int weekday = DateUtils.getWeekday(timestamp); - - return reminderDays[weekday]; + createNotification(context, intent); + createReminderAlarmsDelayed(context); } private void createNotification(final Context context, final Intent intent) @@ -172,7 +132,7 @@ protected void doInBackground() protected void onPostExecute(Void aVoid) { if (todayValue != Checkmark.UNCHECKED) return; - if (!checkWeekday(intent, habit)) return; + if (!shouldShowReminderToday(intent, habit)) return; if (!habit.hasReminder()) return; Intent contentIntent = new Intent(context, MainActivity.class); @@ -185,8 +145,7 @@ protected void onPostExecute(Void aVoid) dismissPendingIntent = HabitPendingIntents.dismissNotification(context); PendingIntent checkIntentPending = - HabitPendingIntents.addCheckmark(context, habit, - timestamp); + HabitPendingIntents.addCheckmark(context, habit, timestamp); PendingIntent snoozeIntentPending = HabitPendingIntents.snoozeNotification(context, habit); @@ -246,7 +205,7 @@ private void dismissNotification(Context context, Long habitId) notificationManager.cancel(notificationId); } - private void snoozeHabit(Context context, Intent intent) + private void onActionSnoozeReminder(Context context, Intent intent) { Uri data = intent.getData(); SharedPreferences prefs = @@ -261,8 +220,18 @@ private void snoozeHabit(Context context, Intent intent) dismissNotification(context, habitId); } - private void toggleRepetition(Context context, Intent intent) + private boolean shouldShowReminderToday(Intent intent, Habit habit) { - addOrRemoveRepetition(context, intent, false); + if (!habit.hasReminder()) return false; + Reminder reminder = habit.getReminder(); + + Long timestamp = + intent.getLongExtra("timestamp", DateUtils.getStartOfToday()); + + boolean reminderDays[] = + DateUtils.unpackWeekdayList(reminder.getDays()); + int weekday = DateUtils.getWeekday(timestamp); + + return reminderDays[weekday]; } } diff --git a/app/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java b/app/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java new file mode 100644 index 000000000..32adbe88f --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.receivers; + +import android.content.*; +import android.support.annotation.*; +import android.util.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; + +import javax.inject.*; + +/** + * The Android BroadcastReceiver for Loop Habit Tracker. + *

+ * All broadcast messages are received and processed by this class. + */ +public class WidgetReceiver extends BroadcastReceiver +{ + public static final String ACTION_ADD_REPETITION = + "org.isoron.uhabits.ACTION_ADD_REPETITION"; + + public static final String ACTION_DISMISS_REMINDER = + "org.isoron.uhabits.ACTION_DISMISS_REMINDER"; + + public static final String ACTION_REMOVE_REPETITION = + "org.isoron.uhabits.ACTION_REMOVE_REPETITION"; + + public static final String ACTION_TOGGLE_REPETITION = + "org.isoron.uhabits.ACTION_TOGGLE_REPETITION"; + + @Inject + HabitList habits; + + @NonNull + private final IntentParser parser; + + @NonNull + private final ReceiverActions actions; + + public WidgetReceiver() + { + super(); + HabitsApplication.getComponent().inject(this); + parser = new IntentParser(habits); + actions = new ReceiverActions(); + } + + @Override + public void onReceive(final Context context, Intent intent) + { + Log.d("WidgetReceiver", + String.format("Received intent: %s", intent.toString())); + try + { + switch (intent.getAction()) + { + case ACTION_ADD_REPETITION: + onActionAddRepetition(intent); + break; + + case ACTION_TOGGLE_REPETITION: + onActionToggleRepetition(intent); + break; + + case ACTION_REMOVE_REPETITION: + onActionRemoveRepetition(intent); + break; + } + } + catch (RuntimeException e) + { + Log.e("WidgetReceiver", "could not process intent", e); + } + } + + private void onActionAddRepetition(Intent intent) + { + IntentParser.CheckmarkIntentData data; + data = parser.parseCheckmarkIntent(intent); + actions.add_repetition(data.habit, data.timestamp); + } + + private void onActionRemoveRepetition(Intent intent) + { + IntentParser.CheckmarkIntentData data; + data = parser.parseCheckmarkIntent(intent); + actions.remove_repetition(data.habit, data.timestamp); + } + + private void onActionToggleRepetition(Intent intent) + { + IntentParser.CheckmarkIntentData data; + data = parser.parseCheckmarkIntent(intent); + actions.toggle_repetition(data.habit, data.timestamp); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/utils/ReminderUtils.java b/app/src/main/java/org/isoron/uhabits/utils/ReminderUtils.java index 4ecb3fe4d..633b1c553 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/ReminderUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/ReminderUtils.java @@ -32,6 +32,7 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; +import org.isoron.uhabits.receivers.*; import java.text.*; import java.util.*; @@ -64,8 +65,8 @@ public static void createReminderAlarm(Context context, Uri uri = habit.getUri(); - Intent alarmIntent = new Intent(context, HabitBroadcastReceiver.class); - alarmIntent.setAction(HabitBroadcastReceiver.ACTION_SHOW_REMINDER); + Intent alarmIntent = new Intent(context, ReminderReceiver.class); + alarmIntent.setAction(ReminderReceiver.ACTION_SHOW_REMINDER); alarmIntent.setData(uri); alarmIntent.putExtra("timestamp", timestamp); alarmIntent.putExtra("reminderTime", reminderTime);