From e041d9041b34145db0ab9020c7ed54a93b76dd6b Mon Sep 17 00:00:00 2001 From: srikanth r Date: Fri, 4 Aug 2017 23:01:41 +0530 Subject: [PATCH 01/19] Implemented cancel to reminders of habits. --- .../uhabits/intents/PendingIntentFactory.kt | 9 +++++++++ .../notifications/AndroidNotificationTray.kt | 8 ++++++++ .../isoron/uhabits/receivers/WidgetReceiver.java | 8 ++++++++ .../main/res/drawable-hdpi/ic_action_cancel.png | Bin 0 -> 266 bytes .../main/res/drawable-mdpi/ic_action_cancel.png | Bin 0 -> 198 bytes .../main/res/drawable-xhdpi/ic_action_cancel.png | Bin 0 -> 323 bytes .../main/res/drawable-xxhdpi/ic_action_cancel.png | Bin 0 -> 531 bytes .../uhabits/core/ui/widgets/WidgetBehavior.java | 6 ++++++ 8 files changed, 31 insertions(+) create mode 100644 uhabits-android/src/main/res/drawable-hdpi/ic_action_cancel.png create mode 100644 uhabits-android/src/main/res/drawable-mdpi/ic_action_cancel.png create mode 100644 uhabits-android/src/main/res/drawable-xhdpi/ic_action_cancel.png create mode 100644 uhabits-android/src/main/res/drawable-xxhdpi/ic_action_cancel.png diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt index 2758ba934..5a8d42936 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt @@ -54,6 +54,15 @@ class PendingIntentFactory }, FLAG_UPDATE_CURRENT) + fun cancelNotification(habit: Habit): PendingIntent = + PendingIntent.getBroadcast( + context, 3, + Intent(context, WidgetReceiver::class.java).apply { + action = WidgetReceiver.ACTION_CANCEL_REPETITION + data = Uri.parse(habit.uriString) + }, + FLAG_UPDATE_CURRENT) + fun showHabit(habit: Habit): PendingIntent = android.support.v4.app.TaskStackBuilder .create(context) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index 1a13d417f..e269df6cb 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -61,6 +61,12 @@ class AndroidNotificationTray context.getString(R.string.snooze), pendingIntents.snoozeNotification(habit)) + val cancelAction = Action( + R.drawable.ic_action_cancel, + context.getString(android.R.string.no), + pendingIntents.cancelNotification(habit) + ) + val wearableBg = decodeResource(context.resources, R.drawable.stripe) // Even though the set of actions is the same on the phone and @@ -70,6 +76,7 @@ class AndroidNotificationTray .setBackground(wearableBg) .addAction(checkAction) .addAction(snoozeAction) + .addAction(cancelAction) val notification = NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification) @@ -79,6 +86,7 @@ class AndroidNotificationTray .setDeleteIntent(pendingIntents.dismissNotification(habit)) .addAction(checkAction) .addAction(snoozeAction) + .addAction(cancelAction) .setSound(ringtoneManager.getURI()) .extend(wearableExtender) .setWhen(reminderTime) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java index 7074224e8..9f98bd191 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java @@ -46,6 +46,9 @@ public class WidgetReceiver extends BroadcastReceiver public static final String ACTION_REMOVE_REPETITION = "org.isoron.uhabits.ACTION_REMOVE_REPETITION"; + public static final String ACTION_CANCEL_REPETITION = + "org.isoron.uhabits.ACTION_CANCEL_REPETITION"; + public static final String ACTION_TOGGLE_REPETITION = "org.isoron.uhabits.ACTION_TOGGLE_REPETITION"; @@ -88,6 +91,11 @@ public class WidgetReceiver extends BroadcastReceiver controller.onRemoveRepetition(data.getHabit(), data.getTimestamp()); break; + + case ACTION_CANCEL_REPETITION: + controller.onCancelRepetition(data.getHabit(), + data.getTimestamp()); + break; } } catch (RuntimeException e) diff --git a/uhabits-android/src/main/res/drawable-hdpi/ic_action_cancel.png b/uhabits-android/src/main/res/drawable-hdpi/ic_action_cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..374fc6fc0d86b48017197db225217f0bf7ffb7b5 GIT binary patch literal 266 zcmV+l0rmcgP)wdAb70#le3)R_UzmN0Q6q0t&9QBuhSYHQEr?y1tr#BVmb zHcZfb`&5&u3MGA;ip&)Khi=lP$DkIi|Im@3g3v$FC$HCRTW9HaxmOHp@ammd5!K5dX1VahO+0asTPU*P*_oB5vBm}WXRV9e@lNt#qQ&!hJcnIX yGRjU|o;A%yXrAcdyRENl-ZULq_x|DWuS~1%wLOY;eRUD&Fa}RoKbLh*2~7aLE>hqC literal 0 HcmV?d00001 diff --git a/uhabits-android/src/main/res/drawable-xhdpi/ic_action_cancel.png b/uhabits-android/src/main/res/drawable-xhdpi/ic_action_cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..dbd10906c98145ac66e6aa0ddbf8a75e1c57a3c3 GIT binary patch literal 323 zcmV-J0lfZ+P)c=K!vDFrqV-m%SUtywr@n1}ov9KFW zxW5!1Sy;C0q$NHnc1Vd|dd|DzClPzYro;|)@f_JnOT6U_fRYOveMxbCPl$^zW)d8! zQq2Tso>wtJ&5xY%U?Fx4IStfQXUM#k**s}}2Y$^i)0000W#~uA} Vg-Jr&F;D;i002ovPDHLkV1iXP)NLv1@zA{!f}MHcyUcGLxzT z0000000000006)PA;eRnb?a1XFWKZ)#zn?U0yzg6XBlg%ayBwP#UHnrl9ZfIt$mg8 zO!4GAss1$c2fVV|AX|<7_K{1m_^*<*I)}fU5RwdX zI(Bj_?<6Zp7INB(GluLoTTB}2oZoDf!^^8v31E}Tb9%#HB#CXE6G2j=>YOr8j$?Mq zCzS`1RJI%kN#)5INV?C+;a}$jeDRFdbfYR~E|mxGC*>liJV_QsV9dzH3;Zf-AS?MR;)^ZR6{*BlGNufA1Q`ns2O literal 0 HcmV?d00001 diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java index 1f9500123..4615a2487 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java @@ -61,6 +61,12 @@ public class WidgetBehavior performToggle(habit, timestamp); } + public void onCancelRepetition(@NonNull Habit habit, Timestamp timestamp) + { + onRemoveRepetition(habit, timestamp); + notificationTray.cancel(habit); + } + public void onToggleRepetition(@NonNull Habit habit, Timestamp timestamp) { performToggle(habit, timestamp); From 87cf2871a7e98c3d8d153f56cd43e725aeea7ab9 Mon Sep 17 00:00:00 2001 From: TruffelNL <16107161+TruffelNL@users.noreply.github.com> Date: Tue, 15 Aug 2017 23:02:58 +0200 Subject: [PATCH 02/19] Added bundled notifications Added bundled/grouped/stacked notifications as per https://github.com/iSoron/uhabits/issues/243 --- .../notifications/AndroidNotificationTray.kt | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index 1a13d417f..054b41ec1 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -22,6 +22,7 @@ package org.isoron.uhabits.notifications import android.app.* import android.content.* import android.graphics.BitmapFactory.* +import android.support.annotation.NonNull import android.support.v4.app.* import android.support.v4.app.NotificationCompat.* import org.isoron.androidbase.* @@ -40,16 +41,44 @@ class AndroidNotificationTray private val pendingIntents: PendingIntentFactory, private val preferences: Preferences, private val ringtoneManager: RingtoneManager + ) : NotificationTray.SystemTray { - override fun removeNotification(id: Int) { + val generalLoopNotificationGroup = "generalLoopHabitsNotificationGroup" + + var summaryShown = false + + override fun removeNotification(id: Int) + { NotificationManagerCompat.from(context).cancel(id) } override fun showNotification(habit: Habit, notificationId: Int, timestamp: Timestamp, - reminderTime: Long) { + reminderTime: Long) + { + val notificationManager = NotificationManagerCompat.from(context) + + if(! summaryShown) + { + val summary = buildSummary(reminderTime) + + notificationManager.notify(Int.MAX_VALUE, summary) + + summaryShown = true + } + + val notification = buildNotification(habit, reminderTime, timestamp) + + notificationManager.notify(notificationId, notification) + } + + @NonNull + fun buildNotification(@NonNull habit: Habit, + @NonNull reminderTime: Long, + @NonNull timestamp: Timestamp) : Notification + { val checkAction = Action( R.drawable.ic_action_check, @@ -71,7 +100,7 @@ class AndroidNotificationTray .addAction(checkAction) .addAction(snoozeAction) - val notification = NotificationCompat.Builder(context) + return NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(habit.name) .setContentText(habit.description) @@ -84,11 +113,20 @@ class AndroidNotificationTray .setWhen(reminderTime) .setShowWhen(true) .setOngoing(preferences.shouldMakeNotificationsSticky()) + .setGroup(generalLoopNotificationGroup) .build() + } - val notificationManager = context.getSystemService( - Activity.NOTIFICATION_SERVICE) as NotificationManager - - notificationManager.notify(notificationId, notification) + @NonNull + fun buildSummary(@NonNull reminderTime: Long) : Notification + { + return NotificationCompat.Builder(context) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(context.getString(R.string.app_name)) + .setWhen(reminderTime) + .setShowWhen(true) + .setGroup(generalLoopNotificationGroup) + .setGroupSummary(true) + .build() } } From 1c2abb543bc360428e6d1006610d21c52b6453de Mon Sep 17 00:00:00 2001 From: TruffelNL <16107161+TruffelNL@users.noreply.github.com> Date: Tue, 15 Aug 2017 23:09:19 +0200 Subject: [PATCH 03/19] Added private variables --- .../isoron/uhabits/notifications/AndroidNotificationTray.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index 054b41ec1..cb267326a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -44,9 +44,9 @@ class AndroidNotificationTray ) : NotificationTray.SystemTray { - val generalLoopNotificationGroup = "generalLoopHabitsNotificationGroup" + private val generalLoopNotificationGroup = "generalLoopHabitsNotificationGroup" - var summaryShown = false + private var summaryShown = false override fun removeNotification(id: Int) { From f6620be2d9a5633d2a27cfed96f813a6923fcf49 Mon Sep 17 00:00:00 2001 From: TruffelNL <16107161+TruffelNL@users.noreply.github.com> Date: Tue, 15 Aug 2017 23:44:41 +0200 Subject: [PATCH 04/19] Removed an if statement Removed the check if a summary was already shown. --- .../uhabits/notifications/AndroidNotificationTray.kt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index cb267326a..5240605bb 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -46,8 +46,6 @@ class AndroidNotificationTray private val generalLoopNotificationGroup = "generalLoopHabitsNotificationGroup" - private var summaryShown = false - override fun removeNotification(id: Int) { NotificationManagerCompat.from(context).cancel(id) @@ -60,14 +58,9 @@ class AndroidNotificationTray { val notificationManager = NotificationManagerCompat.from(context) - if(! summaryShown) - { - val summary = buildSummary(reminderTime) - - notificationManager.notify(Int.MAX_VALUE, summary) + val summary = buildSummary(reminderTime) - summaryShown = true - } + notificationManager.notify(Int.MAX_VALUE, summary) val notification = buildNotification(habit, reminderTime, timestamp) From 43e802fb8e7dfe0a94f85584345a8a58df1fe5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Wed, 13 Sep 2017 16:25:24 +0200 Subject: [PATCH 05/19] implement custom snooze for notifications --- uhabits-android/src/main/AndroidManifest.xml | 1 + .../notifications/SnoozeDelayActivity.java | 85 +++++++++++++++++++ .../uhabits/receivers/ReminderController.java | 24 +++++- .../uhabits/receivers/ReminderReceiver.java | 10 ++- .../src/main/res/values/constants.xml | 2 + .../src/main/res/values/strings.xml | 1 + .../receivers/ReminderControllerTest.java | 2 +- 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java diff --git a/uhabits-android/src/main/AndroidManifest.xml b/uhabits-android/src/main/AndroidManifest.xml index b7cbb4520..ca802a5d6 100644 --- a/uhabits-android/src/main/AndroidManifest.xml +++ b/uhabits-android/src/main/AndroidManifest.xml @@ -91,6 +91,7 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activities.habits.list.ListHabitsActivity"/> + time) + time += DateUtils.DAY_LENGTH; + time = applyTimezone(time); + + Intent intent = new Intent( ReminderReceiver.ACTION_SNOOZE_REMINDER_SET, getIntent().getData(), + this, ReminderReceiver.class ); + intent.putExtra("reminderTime", time); + sendBroadcast(intent); + + finish(); + } + + @Override + public void onTimeCleared(RadialPickerLayout view) + { + Intent intent = new Intent( ReminderReceiver.ACTION_DISMISS_REMINDER, getIntent().getData(), + this, ReminderReceiver.class ); + sendBroadcast(intent); + + finish(); + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java index ae88f55c9..179b16e39 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java @@ -19,12 +19,15 @@ package org.isoron.uhabits.receivers; +import android.content.*; +import android.net.*; import android.support.annotation.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.reminders.*; import org.isoron.uhabits.core.ui.*; +import org.isoron.uhabits.notifications.*; import javax.inject.*; @@ -65,13 +68,32 @@ public class ReminderController reminderScheduler.scheduleAll(); } - public void onSnooze(@NonNull Habit habit) + public void onSnooze(@NonNull Habit habit, final Context context) { long snoozeInterval = preferences.getSnoozeInterval(); + if (snoozeInterval < 0) + { + context.sendBroadcast( new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + Intent intent = new Intent( SnoozeDelayActivity.ACTION_ASK_SNOOZE, Uri.parse( habit.getUriString()), + context, SnoozeDelayActivity.class ); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + return; + } + + snoozeNotificationAddDelay(habit, snoozeInterval); + } + + public void snoozeNotificationAddDelay(@NonNull Habit habit, long snoozeInterval) + { long now = applyTimezone(getLocalTime()); long reminderTime = now + snoozeInterval * 60 * 1000; + snoozeNotificationSetReminderTime(habit, reminderTime); + } + public void snoozeNotificationSetReminderTime(@NonNull Habit habit, long reminderTime) + { reminderScheduler.schedule(habit, reminderTime); notificationTray.cancel(habit); } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index cb382a950..c6990b4dd 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -46,6 +46,9 @@ public class ReminderReceiver extends BroadcastReceiver public static final String ACTION_SNOOZE_REMINDER = "org.isoron.uhabits.ACTION_SNOOZE_REMINDER"; + public static final String ACTION_SNOOZE_REMINDER_SET = + "org.isoron.uhabits.ACTION_SNOOZE_REMINDER_SET"; + private static final String TAG = "ReminderReceiver"; @Override @@ -90,7 +93,12 @@ public class ReminderReceiver extends BroadcastReceiver case ACTION_SNOOZE_REMINDER: if (habit == null) return; - reminderController.onSnooze(habit); + reminderController.onSnooze(habit,context); + break; + + case ACTION_SNOOZE_REMINDER_SET: + if (habit == null) return; + reminderController.snoozeNotificationSetReminderTime(habit, reminderTime); break; case Intent.ACTION_BOOT_COMPLETED: diff --git a/uhabits-android/src/main/res/values/constants.xml b/uhabits-android/src/main/res/values/constants.xml index 94ae54340..1b46ccc5d 100644 --- a/uhabits-android/src/main/res/values/constants.xml +++ b/uhabits-android/src/main/res/values/constants.xml @@ -35,6 +35,7 @@ @string/interval_4_hour @string/interval_8_hour @string/interval_24_hour + @string/interval_always_ask @@ -45,6 +46,7 @@ 240 480 1440 + -1 diff --git a/uhabits-android/src/main/res/values/strings.xml b/uhabits-android/src/main/res/values/strings.xml index 1671734fd..821cf2298 100644 --- a/uhabits-android/src/main/res/values/strings.xml +++ b/uhabits-android/src/main/res/values/strings.xml @@ -83,6 +83,7 @@ 4 hours 8 hours 24 hours + Always ask Toggle with short press Put checkmarks with a single tap instead of press-and-hold. More convenient, but might cause accidental toggles. Snooze interval on reminders diff --git a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java index 93b199a10..7b0406832 100644 --- a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java +++ b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java @@ -70,7 +70,7 @@ public class ReminderControllerTest extends BaseAndroidJVMTest DateUtils.setFixedLocalTime(now); when(preferences.getSnoozeInterval()).thenReturn(15L); - controller.onSnooze(habit); + controller.onSnooze(habit,null); verify(reminderScheduler).schedule(habit, nowTz + 900000); verify(notificationTray).cancel(habit); From 0421ca054965816cfb31f06e7095074ee2008a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Thu, 14 Sep 2017 10:32:59 +0200 Subject: [PATCH 06/19] make snooze time dialog follow night mode setting --- .../org/isoron/uhabits/notifications/SnoozeDelayActivity.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java index 2de2c2ae7..7a3d27b5f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java @@ -8,11 +8,13 @@ import android.util.*; import com.android.datetimepicker.time.*; +import org.isoron.uhabits.*; import org.isoron.uhabits.core.utils.DateUtils; import org.isoron.uhabits.receivers.*; import java.util.*; +import static org.isoron.uhabits.core.ui.ThemeSwitcher.THEME_DARK; import static org.isoron.uhabits.core.utils.DateUtils.applyTimezone; public class SnoozeDelayActivity extends FragmentActivity implements @@ -50,6 +52,8 @@ public class SnoozeDelayActivity extends FragmentActivity implements TimePickerDialog dialog; dialog = TimePickerDialog.newInstance(this, hour, minute, DateFormat.is24HourFormat(this)); + HabitsApplicationComponent component = ((HabitsApplication) getApplicationContext()).getComponent(); + dialog.setThemeDark(component.getPreferences().getTheme() == THEME_DARK); dialog.show(getSupportFragmentManager(),"timePicker"); } From aac59367dcc4b19d09972dbb4523db98d4a206f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Thu, 14 Sep 2017 13:02:05 +0200 Subject: [PATCH 07/19] properly handle the activity window of the snooze time dialog --- .../datetimepicker/time/TimePickerDialog.java | 12 ++++++++++++ uhabits-android/src/main/AndroidManifest.xml | 6 +++++- .../uhabits/notifications/SnoozeDelayActivity.java | 9 ++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java b/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java index 0bbb741e2..7cbdd3cd3 100644 --- a/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java +++ b/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java @@ -63,6 +63,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue private static final int PULSE_ANIMATOR_DELAY = 300; private OnTimeSetListener mCallback; + private DialogInterface.OnDismissListener dismissListener; private HapticFeedbackController mHapticFeedbackController; @@ -998,4 +999,15 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue return false; } } + + public void setDismissListener( DialogInterface.OnDismissListener listener ) { + dismissListener = listener; + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if( dismissListener != null ) + dismissListener.onDismiss(dialog); + } } diff --git a/uhabits-android/src/main/AndroidManifest.xml b/uhabits-android/src/main/AndroidManifest.xml index ca802a5d6..99e5b20fb 100644 --- a/uhabits-android/src/main/AndroidManifest.xml +++ b/uhabits-android/src/main/AndroidManifest.xml @@ -91,7 +91,11 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activities.habits.list.ListHabitsActivity"/> - + + Date: Fri, 15 Sep 2017 11:29:36 +0200 Subject: [PATCH 08/19] when asking for snooze delay, first show a list of common options --- .../notifications/SnoozeDelayActivity.java | 34 ++++++++++++++++++- .../uhabits/receivers/ReminderReceiver.java | 9 +++++ .../src/main/res/values/constants.xml | 22 ++++++++++++ .../src/main/res/values/strings.xml | 2 ++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java index a32871f65..be0ef36d1 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java @@ -1,5 +1,6 @@ package org.isoron.uhabits.notifications; +import android.app.*; import android.content.*; import android.os.*; import android.support.v4.app.*; @@ -7,6 +8,7 @@ import android.text.format.*; import android.util.*; import com.android.datetimepicker.time.*; +import com.android.datetimepicker.time.TimePickerDialog; import org.isoron.uhabits.*; import org.isoron.uhabits.core.utils.DateUtils; @@ -18,12 +20,14 @@ import static org.isoron.uhabits.core.ui.ThemeSwitcher.THEME_DARK; import static org.isoron.uhabits.core.utils.DateUtils.applyTimezone; public class SnoozeDelayActivity extends FragmentActivity implements - TimePickerDialog.OnTimeSetListener, DialogInterface.OnDismissListener { + TimePickerDialog.OnTimeSetListener, DialogInterface.OnDismissListener, DialogInterface.OnClickListener { public static final String ACTION_ASK_SNOOZE = "org.isoron.uhabits.ACTION_ASK_SNOOZE"; private static final String TAG = "SnoozeDelayActivity"; + private AlertDialog activeDelayDialog; + @Override protected void onCreate(Bundle bundle) { @@ -45,6 +49,34 @@ public class SnoozeDelayActivity extends FragmentActivity implements } private void AskSnooze() + { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.select_snooze_delay) + .setItems(R.array.snooze_interval_names_reminder, this); + AlertDialog dialog = builder.create(); + dialog.setOnDismissListener(this); + activeDelayDialog = dialog; + dialog.show(); + } + + @Override + public void onClick(DialogInterface dialogInterface, int i) + { + int[] snoozeDelay = getResources().getIntArray(R.array.snooze_interval_values_reminder); + assert (i >= 0 && i <= snoozeDelay.length); + if( snoozeDelay[ i ] < 0 ) + { + activeDelayDialog.setOnDismissListener(null); + AskCustomSnooze(); + return; + } + Intent intent = new Intent( ReminderReceiver.ACTION_SNOOZE_REMINDER_DELAY, getIntent().getData(), + this, ReminderReceiver.class ); + intent.putExtra("snoozeDelay", snoozeDelay[ i ]); + sendBroadcast(intent); + } + + private void AskCustomSnooze() { final Calendar calendar = Calendar.getInstance(); int hour = calendar.get(Calendar.HOUR_OF_DAY); diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index c6990b4dd..b80baf11b 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -49,6 +49,9 @@ public class ReminderReceiver extends BroadcastReceiver public static final String ACTION_SNOOZE_REMINDER_SET = "org.isoron.uhabits.ACTION_SNOOZE_REMINDER_SET"; + public static final String ACTION_SNOOZE_REMINDER_DELAY = + "org.isoron.uhabits.ACTION_SNOOZE_REMINDER_DELAY"; + private static final String TAG = "ReminderReceiver"; @Override @@ -101,6 +104,12 @@ public class ReminderReceiver extends BroadcastReceiver reminderController.snoozeNotificationSetReminderTime(habit, reminderTime); break; + case ACTION_SNOOZE_REMINDER_DELAY: + if (habit == null) return; + long snoozeDelay = intent.getIntExtra("snoozeDelay", 0); + reminderController.snoozeNotificationAddDelay(habit, snoozeDelay); + break; + case Intent.ACTION_BOOT_COMPLETED: reminderController.onBootCompleted(); break; diff --git a/uhabits-android/src/main/res/values/constants.xml b/uhabits-android/src/main/res/values/constants.xml index 1b46ccc5d..e8d08c0b5 100644 --- a/uhabits-android/src/main/res/values/constants.xml +++ b/uhabits-android/src/main/res/values/constants.xml @@ -49,6 +49,28 @@ -1 + + @string/interval_15_minutes + @string/interval_30_minutes + @string/interval_1_hour + @string/interval_2_hour + @string/interval_4_hour + @string/interval_8_hour + @string/interval_24_hour + @string/interval_custom + + + + 15 + 30 + 60 + 120 + 240 + 480 + 1440 + -1 + + @string/every_day @string/every_week diff --git a/uhabits-android/src/main/res/values/strings.xml b/uhabits-android/src/main/res/values/strings.xml index 821cf2298..814a53d32 100644 --- a/uhabits-android/src/main/res/values/strings.xml +++ b/uhabits-android/src/main/res/values/strings.xml @@ -84,6 +84,7 @@ 8 hours 24 hours Always ask + Custom... Toggle with short press Put checkmarks with a single tap instead of press-and-hold. More convenient, but might cause accidental toggles. Snooze interval on reminders @@ -96,6 +97,7 @@ Name Settings Snooze interval + Select snooze delay Did you know? To rearrange the entries, press-and-hold on the name of the habit, then drag it to the correct place. From e970473876838129dd0ae524da11c0352eac255a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Fri, 15 Sep 2017 11:58:58 +0200 Subject: [PATCH 09/19] make notification snooze popup follow night mode setting --- .../isoron/uhabits/notifications/SnoozeDelayActivity.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java index be0ef36d1..769ba7713 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java @@ -4,6 +4,7 @@ import android.app.*; import android.content.*; import android.os.*; import android.support.v4.app.*; +import android.support.v7.view.*; import android.text.format.*; import android.util.*; @@ -50,7 +51,10 @@ public class SnoozeDelayActivity extends FragmentActivity implements private void AskSnooze() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); + HabitsApplicationComponent component = ((HabitsApplication) getApplicationContext()).getComponent(); + int theme = component.getPreferences().getTheme() == THEME_DARK + ? R.style.Theme_AppCompat_Dialog_Alert : R.style.Theme_AppCompat_Light_Dialog_Alert; + AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, theme)); builder.setTitle(R.string.select_snooze_delay) .setItems(R.array.snooze_interval_names_reminder, this); AlertDialog dialog = builder.create(); From a201273781f214da51e42ebee3370ee14b911ee2 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Mon, 9 Oct 2017 19:58:48 -0500 Subject: [PATCH 10/19] Refactoring --- android-pickers/build.gradle | 5 + .../datetimepicker/time/TimePickerDialog.java | 2 +- uhabits-android/src/main/AndroidManifest.xml | 2 +- .../uhabits/HabitsApplicationComponent.java | 3 + .../notifications/SnoozeDelayActivity.java | 128 ------------------ .../SnoozeDelayPickerActivity.java | 85 ++++++++++++ .../uhabits/receivers/ReminderController.java | 56 ++++---- .../uhabits/receivers/ReminderReceiver.java | 55 ++------ .../src/main/res/values/constants.xml | 4 +- .../receivers/ReminderControllerTest.java | 4 +- .../isoron/uhabits/core/models/Reminder.java | 32 +++-- .../core/reminders/ReminderScheduler.java | 32 ++--- .../isoron/uhabits/core/utils/DateUtils.java | 14 ++ 13 files changed, 189 insertions(+), 233 deletions(-) delete mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java diff --git a/android-pickers/build.gradle b/android-pickers/build.gradle index fc75627d8..e88f868cc 100644 --- a/android-pickers/build.gradle +++ b/android-pickers/build.gradle @@ -18,6 +18,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + compileOptions { + targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_8 + } } dependencies { diff --git a/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java b/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java index 7cbdd3cd3..06c121b3c 100644 --- a/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java +++ b/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java @@ -117,7 +117,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue */ void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute); - void onTimeCleared(RadialPickerLayout view); + default void onTimeCleared(RadialPickerLayout view) {} } public TimePickerDialog() { diff --git a/uhabits-android/src/main/AndroidManifest.xml b/uhabits-android/src/main/AndroidManifest.xml index 99e5b20fb..be3dff213 100644 --- a/uhabits-android/src/main/AndroidManifest.xml +++ b/uhabits-android/src/main/AndroidManifest.xml @@ -91,7 +91,7 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activities.habits.list.ListHabitsActivity"/> - diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java index 375d2db34..3fd635f22 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java @@ -33,6 +33,7 @@ import org.isoron.uhabits.core.ui.*; import org.isoron.uhabits.core.ui.screens.habits.list.*; import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.intents.*; +import org.isoron.uhabits.receivers.*; import org.isoron.uhabits.sync.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.widgets.*; @@ -80,6 +81,8 @@ public interface HabitsApplicationComponent ReminderScheduler getReminderScheduler(); + ReminderController getReminderController(); + SyncManager getSyncManager(); TaskRunner getTaskRunner(); diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java deleted file mode 100644 index 769ba7713..000000000 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.isoron.uhabits.notifications; - -import android.app.*; -import android.content.*; -import android.os.*; -import android.support.v4.app.*; -import android.support.v7.view.*; -import android.text.format.*; -import android.util.*; - -import com.android.datetimepicker.time.*; -import com.android.datetimepicker.time.TimePickerDialog; - -import org.isoron.uhabits.*; -import org.isoron.uhabits.core.utils.DateUtils; -import org.isoron.uhabits.receivers.*; - -import java.util.*; - -import static org.isoron.uhabits.core.ui.ThemeSwitcher.THEME_DARK; -import static org.isoron.uhabits.core.utils.DateUtils.applyTimezone; - -public class SnoozeDelayActivity extends FragmentActivity implements - TimePickerDialog.OnTimeSetListener, DialogInterface.OnDismissListener, DialogInterface.OnClickListener { - - public static final String ACTION_ASK_SNOOZE = "org.isoron.uhabits.ACTION_ASK_SNOOZE"; - - private static final String TAG = "SnoozeDelayActivity"; - - private AlertDialog activeDelayDialog; - - @Override - protected void onCreate(Bundle bundle) - { - super.onCreate(bundle); - Intent intent = getIntent(); - try - { - switch (intent.getAction()) - { - case ACTION_ASK_SNOOZE: - AskSnooze(); - break; - } - } - catch (RuntimeException e) - { - Log.e(TAG, "could not process intent", e); - } - } - - private void AskSnooze() - { - HabitsApplicationComponent component = ((HabitsApplication) getApplicationContext()).getComponent(); - int theme = component.getPreferences().getTheme() == THEME_DARK - ? R.style.Theme_AppCompat_Dialog_Alert : R.style.Theme_AppCompat_Light_Dialog_Alert; - AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, theme)); - builder.setTitle(R.string.select_snooze_delay) - .setItems(R.array.snooze_interval_names_reminder, this); - AlertDialog dialog = builder.create(); - dialog.setOnDismissListener(this); - activeDelayDialog = dialog; - dialog.show(); - } - - @Override - public void onClick(DialogInterface dialogInterface, int i) - { - int[] snoozeDelay = getResources().getIntArray(R.array.snooze_interval_values_reminder); - assert (i >= 0 && i <= snoozeDelay.length); - if( snoozeDelay[ i ] < 0 ) - { - activeDelayDialog.setOnDismissListener(null); - AskCustomSnooze(); - return; - } - Intent intent = new Intent( ReminderReceiver.ACTION_SNOOZE_REMINDER_DELAY, getIntent().getData(), - this, ReminderReceiver.class ); - intent.putExtra("snoozeDelay", snoozeDelay[ i ]); - sendBroadcast(intent); - } - - private void AskCustomSnooze() - { - final Calendar calendar = Calendar.getInstance(); - int hour = calendar.get(Calendar.HOUR_OF_DAY); - int minute = calendar.get(Calendar.MINUTE); - TimePickerDialog dialog; - dialog = TimePickerDialog.newInstance(this, - hour, minute, DateFormat.is24HourFormat(this)); - HabitsApplicationComponent component = ((HabitsApplication) getApplicationContext()).getComponent(); - dialog.setThemeDark(component.getPreferences().getTheme() == THEME_DARK); - dialog.setDismissListener(this); - dialog.show(getSupportFragmentManager(),"timePicker"); - } - - @Override - public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) - { - Calendar calendar = DateUtils.getStartOfTodayCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); - calendar.set(Calendar.MINUTE, minute); - calendar.set(Calendar.SECOND, 0); - Long time = calendar.getTimeInMillis(); - if (DateUtils.getLocalTime() > time) - time += DateUtils.DAY_LENGTH; - time = applyTimezone(time); - - Intent intent = new Intent( ReminderReceiver.ACTION_SNOOZE_REMINDER_SET, getIntent().getData(), - this, ReminderReceiver.class ); - intent.putExtra("reminderTime", time); - sendBroadcast(intent); - } - - @Override - public void onTimeCleared(RadialPickerLayout view) - { - Intent intent = new Intent( ReminderReceiver.ACTION_DISMISS_REMINDER, getIntent().getData(), - this, ReminderReceiver.class ); - sendBroadcast(intent); - } - - @Override - public void onDismiss(DialogInterface dialogInterface) - { - finish(); - } -} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java new file mode 100644 index 000000000..ab51a7983 --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java @@ -0,0 +1,85 @@ +package org.isoron.uhabits.notifications; + +import android.os.*; +import android.support.annotation.*; +import android.support.v4.app.*; +import android.support.v7.app.*; +import android.text.format.*; +import android.view.*; +import android.widget.*; + +import com.android.datetimepicker.time.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.R; +import org.isoron.uhabits.core.models.*; +import org.isoron.uhabits.receivers.*; + +import java.util.*; + +import butterknife.*; + +import static android.content.ContentUris.*; + +public class SnoozeDelayPickerActivity extends AppCompatActivity implements AdapterView + .OnItemClickListener +{ + public static final String ACTION_ASK_SNOOZE = "org.isoron.uhabits.ACTION_ASK_SNOOZE"; + + private Habit habit; + + private ReminderController reminderController; + + @BindArray(R.array.snooze_picker_names) + protected String[] snoozeNames; + + @BindArray(R.array.snooze_picker_values) + protected int[] snoozeValues; + + @Override + protected void onCreate(@Nullable Bundle bundle) + { + super.onCreate(bundle); + if (getIntent() == null) finish(); + if (getIntent().getData() == null) finish(); + + HabitsApplication app = (HabitsApplication) getApplicationContext(); + HabitsApplicationComponent appComponent = app.getComponent(); + reminderController = appComponent.getReminderController(); + habit = appComponent.getHabitList().getById(parseId(getIntent().getData())); + if (habit == null) finish(); + + setTheme(R.style.Theme_AppCompat_Light_Dialog_Alert); + ButterKnife.bind(this); + + ListView listView = new ListView(this); + listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, + snoozeNames)); + listView.setOnItemClickListener(this); + setContentView(listView); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) + { + if (snoozeValues[position] >= 0) + { + reminderController.onSnoozeDelayPicked(habit, snoozeValues[position]); + finish(); + } + else showTimePicker(); + } + + private void showTimePicker() + { + final Calendar calendar = Calendar.getInstance(); + int defaultHour = calendar.get(Calendar.HOUR_OF_DAY); + int defaultMinute = calendar.get(Calendar.MINUTE); + TimePickerDialog dialog = TimePickerDialog.newInstance( + (view, hour, minute) -> + reminderController.onSnoozeTimePicked(habit, hour, minute), + defaultHour, defaultMinute, DateFormat.is24HourFormat(this)); + + dialog.show(getSupportFragmentManager(), "timePicker"); + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java index 179b16e39..0d07a1212 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java @@ -23,18 +23,17 @@ import android.content.*; import android.net.*; import android.support.annotation.*; +import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.reminders.*; import org.isoron.uhabits.core.ui.*; +import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.notifications.*; import javax.inject.*; -import static org.isoron.uhabits.core.utils.DateUtils.applyTimezone; -import static org.isoron.uhabits.core.utils.DateUtils.getLocalTime; - -@ReceiverScope +@AppScope public class ReminderController { @NonNull @@ -43,6 +42,7 @@ public class ReminderController @NonNull private final NotificationTray notificationTray; + @NonNull private Preferences preferences; @Inject @@ -68,33 +68,25 @@ public class ReminderController reminderScheduler.scheduleAll(); } - public void onSnooze(@NonNull Habit habit, final Context context) + public void onSnoozePressed(@NonNull Habit habit, final Context context) { - long snoozeInterval = preferences.getSnoozeInterval(); - - if (snoozeInterval < 0) - { - context.sendBroadcast( new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - Intent intent = new Intent( SnoozeDelayActivity.ACTION_ASK_SNOOZE, Uri.parse( habit.getUriString()), - context, SnoozeDelayActivity.class ); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - return; - } - - snoozeNotificationAddDelay(habit, snoozeInterval); + long delay = preferences.getSnoozeInterval(); + + if (delay < 0) + showSnoozeDelayPicker(habit, context); + else + scheduleReminderMinutesFromNow(habit, delay); } - public void snoozeNotificationAddDelay(@NonNull Habit habit, long snoozeInterval) + public void onSnoozeDelayPicked(Habit habit, int delay) { - long now = applyTimezone(getLocalTime()); - long reminderTime = now + snoozeInterval * 60 * 1000; - snoozeNotificationSetReminderTime(habit, reminderTime); + scheduleReminderMinutesFromNow(habit, delay); } - public void snoozeNotificationSetReminderTime(@NonNull Habit habit, long reminderTime) + public void onSnoozeTimePicked(Habit habit, int hour, int minute) { - reminderScheduler.schedule(habit, reminderTime); + Long time = DateUtils.getUpcomingTimeInMillis(hour, minute); + reminderScheduler.scheduleAtTime(habit, time); notificationTray.cancel(habit); } @@ -102,4 +94,20 @@ public class ReminderController { notificationTray.cancel(habit); } + + private void scheduleReminderMinutesFromNow(Habit habit, long minutes) + { + reminderScheduler.scheduleMinutesFromNow(habit, minutes); + notificationTray.cancel(habit); + } + + private void showSnoozeDelayPicker(@NonNull Habit habit, Context context) + { + context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + Intent intent = new Intent(SnoozeDelayPickerActivity.ACTION_ASK_SNOOZE, + Uri.parse(habit.getUriString()), + context, SnoozeDelayPickerActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index b80baf11b..dff612345 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -20,14 +20,13 @@ package org.isoron.uhabits.receivers; import android.content.*; +import android.support.annotation.*; import android.util.*; import org.isoron.uhabits.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.utils.*; -import dagger.*; - import static android.content.ContentUris.*; /** @@ -38,36 +37,26 @@ import static android.content.ContentUris.*; public class ReminderReceiver extends BroadcastReceiver { public static final String ACTION_DISMISS_REMINDER = - "org.isoron.uhabits.ACTION_DISMISS_REMINDER"; + "org.isoron.uhabits.ACTION_DISMISS_REMINDER"; public static final String ACTION_SHOW_REMINDER = - "org.isoron.uhabits.ACTION_SHOW_REMINDER"; + "org.isoron.uhabits.ACTION_SHOW_REMINDER"; public static final String ACTION_SNOOZE_REMINDER = - "org.isoron.uhabits.ACTION_SNOOZE_REMINDER"; - - public static final String ACTION_SNOOZE_REMINDER_SET = - "org.isoron.uhabits.ACTION_SNOOZE_REMINDER_SET"; - - public static final String ACTION_SNOOZE_REMINDER_DELAY = - "org.isoron.uhabits.ACTION_SNOOZE_REMINDER_DELAY"; + "org.isoron.uhabits.ACTION_SNOOZE_REMINDER"; private static final String TAG = "ReminderReceiver"; @Override - public void onReceive(final Context context, Intent intent) + public void onReceive(@Nullable final Context context, @Nullable Intent intent) { - HabitsApplication app = - (HabitsApplication) context.getApplicationContext(); - - ReminderComponent component = DaggerReminderReceiver_ReminderComponent - .builder() - .habitsApplicationComponent(app.getComponent()) - .build(); + if (context == null || intent == null) return; + if (intent.getAction() == null) return; - HabitList habits = app.getComponent().getHabitList(); - ReminderController reminderController = - component.getReminderController(); + HabitsApplication app = (HabitsApplication) context.getApplicationContext(); + HabitsApplicationComponent appComponent = app.getComponent(); + HabitList habits = appComponent.getHabitList(); + ReminderController reminderController = appComponent.getReminderController(); Log.i(TAG, String.format("Received intent: %s", intent.toString())); @@ -86,7 +75,7 @@ public class ReminderReceiver extends BroadcastReceiver case ACTION_SHOW_REMINDER: if (habit == null) return; reminderController.onShowReminder(habit, - new Timestamp(timestamp), reminderTime); + new Timestamp(timestamp), reminderTime); break; case ACTION_DISMISS_REMINDER: @@ -96,18 +85,7 @@ public class ReminderReceiver extends BroadcastReceiver case ACTION_SNOOZE_REMINDER: if (habit == null) return; - reminderController.onSnooze(habit,context); - break; - - case ACTION_SNOOZE_REMINDER_SET: - if (habit == null) return; - reminderController.snoozeNotificationSetReminderTime(habit, reminderTime); - break; - - case ACTION_SNOOZE_REMINDER_DELAY: - if (habit == null) return; - long snoozeDelay = intent.getIntExtra("snoozeDelay", 0); - reminderController.snoozeNotificationAddDelay(habit, snoozeDelay); + reminderController.onSnoozePressed(habit, context); break; case Intent.ACTION_BOOT_COMPLETED: @@ -120,11 +98,4 @@ public class ReminderReceiver extends BroadcastReceiver Log.e(TAG, "could not process intent", e); } } - - @ReceiverScope - @Component(dependencies = HabitsApplicationComponent.class) - interface ReminderComponent - { - ReminderController getReminderController(); - } } diff --git a/uhabits-android/src/main/res/values/constants.xml b/uhabits-android/src/main/res/values/constants.xml index e8d08c0b5..34e8136a5 100644 --- a/uhabits-android/src/main/res/values/constants.xml +++ b/uhabits-android/src/main/res/values/constants.xml @@ -49,7 +49,7 @@ -1 - + @string/interval_15_minutes @string/interval_30_minutes @string/interval_1_hour @@ -60,7 +60,7 @@ @string/interval_custom - + 15 30 60 diff --git a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java index 7b0406832..69528118a 100644 --- a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java +++ b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java @@ -70,9 +70,9 @@ public class ReminderControllerTest extends BaseAndroidJVMTest DateUtils.setFixedLocalTime(now); when(preferences.getSnoozeInterval()).thenReturn(15L); - controller.onSnooze(habit,null); + controller.onSnoozePressed(habit,null); - verify(reminderScheduler).schedule(habit, nowTz + 900000); + verify(reminderScheduler).scheduleAtTime(habit, nowTz + 900000); verify(notificationTray).cancel(habit); } diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java index 870954605..8cb0d31f0 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java @@ -22,8 +22,9 @@ package org.isoron.uhabits.core.models; import android.support.annotation.*; import org.apache.commons.lang3.builder.*; +import org.isoron.uhabits.core.utils.*; -import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle; +import static org.isoron.uhabits.core.utils.StringUtils.*; public final class Reminder { @@ -56,6 +57,11 @@ public final class Reminder return minute; } + public long getTimeInMillis() + { + return DateUtils.getUpcomingTimeInMillis(hour, minute); + } + @Override public boolean equals(Object o) { @@ -66,29 +72,29 @@ public final class Reminder Reminder reminder = (Reminder) o; return new EqualsBuilder() - .append(hour, reminder.hour) - .append(minute, reminder.minute) - .append(days, reminder.days) - .isEquals(); + .append(hour, reminder.hour) + .append(minute, reminder.minute) + .append(days, reminder.days) + .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) - .append(hour) - .append(minute) - .append(days) - .toHashCode(); + .append(hour) + .append(minute) + .append(days) + .toHashCode(); } @Override public String toString() { return new ToStringBuilder(this, defaultToStringStyle()) - .append("hour", hour) - .append("minute", minute) - .append("days", days) - .toString(); + .append("hour", hour) + .append("minute", minute) + .append("days", days) + .toString(); } } diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java index e31ed71ed..ce4059ae8 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java @@ -24,9 +24,6 @@ import android.support.annotation.*; import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.commands.*; import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.core.utils.*; - -import java.util.*; import javax.inject.*; @@ -60,14 +57,17 @@ public class ReminderScheduler implements CommandRunner.Listener scheduleAll(); } - public void schedule(@NonNull Habit habit, @Nullable Long reminderTime) + public void schedule(@NonNull Habit habit) + { + Long reminderTime = habit.getReminder().getTimeInMillis(); + scheduleAtTime(habit, reminderTime); + } + + public void scheduleAtTime(@NonNull Habit habit, @NonNull Long reminderTime) { if (!habit.hasReminder()) return; if (habit.isArchived()) return; - Reminder reminder = habit.getReminder(); - if (reminderTime == null) reminderTime = getReminderTime(reminder); long timestamp = getStartOfDay(removeTimezone(reminderTime)); - sys.scheduleShowReminder(reminderTime, habit, timestamp); } @@ -76,7 +76,7 @@ public class ReminderScheduler implements CommandRunner.Listener HabitList reminderHabits = habitList.getFiltered(HabitMatcher.WITH_ALARM); for (Habit habit : reminderHabits) - schedule(habit, null); + schedule(habit); } public void startListening() @@ -89,19 +89,11 @@ public class ReminderScheduler implements CommandRunner.Listener commandRunner.removeListener(this); } - @NonNull - private Long getReminderTime(@NonNull Reminder reminder) + public void scheduleMinutesFromNow(Habit habit, long minutes) { - Calendar calendar = DateUtils.getStartOfTodayCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, reminder.getHour()); - calendar.set(Calendar.MINUTE, reminder.getMinute()); - calendar.set(Calendar.SECOND, 0); - Long time = calendar.getTimeInMillis(); - - if (DateUtils.getLocalTime() > time) - time += DateUtils.DAY_LENGTH; - - return applyTimezone(time); + long now = applyTimezone(getLocalTime()); + long reminderTime = now + minutes * 60 * 1000; + scheduleAtTime(habit, reminderTime); } public interface SystemScheduler diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java index ff52f47db..149af5969 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java @@ -240,6 +240,20 @@ public abstract class DateUtils } } + public static long getUpcomingTimeInMillis(int hour, int minute) + { + Calendar calendar = DateUtils.getStartOfTodayCalendar(); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, 0); + Long time = calendar.getTimeInMillis(); + + if (DateUtils.getLocalTime() > time) + time += DateUtils.DAY_LENGTH; + + return applyTimezone(time); + } + public enum TruncateField { MONTH, WEEK_NUMBER, YEAR, QUARTER From 1edd76ae8c495f09f558f3ecb72f906e2fd56647 Mon Sep 17 00:00:00 2001 From: derebaba Date: Thu, 2 Nov 2017 14:21:27 +0300 Subject: [PATCH 11/19] Added delete button to statistics screen --- .../habits/show/ShowHabitScreen.java | 20 ++++++++++++- .../habits/show/ShowHabitsMenu.java | 4 +++ .../src/main/res/menu/show_habit.xml | 5 ++++ .../habits/show/ShowHabitMenuBehavior.java | 29 +++++++++++++++++-- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java index eeb11c6aa..a929d25b9 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java @@ -27,6 +27,7 @@ import org.isoron.uhabits.activities.common.dialogs.*; import org.isoron.uhabits.activities.habits.edit.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.ui.screens.habits.show.*; +import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback; import javax.inject.*; @@ -45,6 +46,9 @@ public class ShowHabitScreen extends BaseScreen @NonNull private final EditHabitDialogFactory editHabitDialogFactory; + @NonNull + private final ConfirmDeleteDialogFactory confirmDeleteDialogFactory; + private final Lazy behavior; @Inject @@ -53,10 +57,12 @@ public class ShowHabitScreen extends BaseScreen @NonNull ShowHabitRootView view, @NonNull ShowHabitsMenu menu, @NonNull - EditHabitDialogFactory editHabitDialogFactory, + EditHabitDialogFactory editHabitDialogFactory, + @NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory, @NonNull Lazy behavior) { super(activity); + this.confirmDeleteDialogFactory = confirmDeleteDialogFactory; setMenu(menu); setRootView(view); @@ -116,6 +122,18 @@ public class ShowHabitScreen extends BaseScreen { case COULD_NOT_EXPORT: showMessage(R.string.could_not_export); + case HABIT_DELETED: + showMessage(R.string.delete_habits_message); } } + + @Override + public void showDeleteConfirmationScreen(OnConfirmedCallback callback) { + activity.showDialog(confirmDeleteDialogFactory.create(callback)); + } + + @Override + public void endActivity() { + activity.finish(); + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java index fe30a3e31..733db302c 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java @@ -57,6 +57,10 @@ public class ShowHabitsMenu extends BaseMenu behavior.get().onExportCSV(); return true; + case R.id.action_delete: + behavior.get().onDeleteHabit(); + return true; + default: return false; } diff --git a/uhabits-android/src/main/res/menu/show_habit.xml b/uhabits-android/src/main/res/menu/show_habit.xml index 366a53e6c..e14ce108c 100644 --- a/uhabits-android/src/main/res/menu/show_habit.xml +++ b/uhabits-android/src/main/res/menu/show_habit.xml @@ -26,6 +26,11 @@ android:title="@string/export" app:showAsAction="never"/> + + selected = Collections.singletonList(habit); + + screen.showDeleteConfirmationScreen(() -> { + commandRunner.execute(new DeleteHabitsCommand(habitList, selected), + null); + screen.showMessage(Message.HABIT_DELETED); + screen.endActivity(); + }); + } + public enum Message { - COULD_NOT_EXPORT + COULD_NOT_EXPORT, HABIT_DELETED } public interface Screen @@ -89,6 +109,11 @@ public class ShowHabitMenuBehavior void showMessage(Message m); void showSendFileScreen(String filename); + + void showDeleteConfirmationScreen( + @NonNull OnConfirmedCallback callback); + + void endActivity(); } public interface System From 9d48b4bcdbb1f177fc06e439a9bce2fbab3c0b28 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Tue, 14 Nov 2017 22:07:52 -0600 Subject: [PATCH 12/19] Simplify code; change notification actions to Yes/No/Later --- .../uhabits/intents/PendingIntentFactory.kt | 4 ++-- .../notifications/AndroidNotificationTray.kt | 18 +++++++++--------- .../uhabits/receivers/WidgetReceiver.java | 8 -------- .../src/main/res/values/strings.xml | 2 ++ .../core/ui/widgets/WidgetBehavior.java | 9 ++------- 5 files changed, 15 insertions(+), 26 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt index 5a8d42936..b60904840 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt @@ -54,11 +54,11 @@ class PendingIntentFactory }, FLAG_UPDATE_CURRENT) - fun cancelNotification(habit: Habit): PendingIntent = + fun removeRepetition(habit: Habit): PendingIntent = PendingIntent.getBroadcast( context, 3, Intent(context, WidgetReceiver::class.java).apply { - action = WidgetReceiver.ACTION_CANCEL_REPETITION + action = WidgetReceiver.ACTION_REMOVE_REPETITION data = Uri.parse(habit.uriString) }, FLAG_UPDATE_CURRENT) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index b20e88690..15abc403f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -66,9 +66,9 @@ class AndroidNotificationTray @NonNull timestamp: Timestamp) : Notification { - val checkAction = Action( + val addRepetitionAction = Action( R.drawable.ic_action_check, - context.getString(R.string.check), + context.getString(R.string.yes), pendingIntents.addCheckmark(habit, timestamp)) val snoozeAction = Action( @@ -76,10 +76,10 @@ class AndroidNotificationTray context.getString(R.string.snooze), pendingIntents.snoozeNotification(habit)) - val cancelAction = Action( + val removeRepetitionAction = Action( R.drawable.ic_action_cancel, - context.getString(android.R.string.no), - pendingIntents.cancelNotification(habit) + context.getString(R.string.no), + pendingIntents.removeRepetition(habit) ) val wearableBg = decodeResource(context.resources, R.drawable.stripe) @@ -89,9 +89,9 @@ class AndroidNotificationTray // WearableExtender. val wearableExtender = WearableExtender() .setBackground(wearableBg) - .addAction(checkAction) + .addAction(addRepetitionAction) + .addAction(removeRepetitionAction) .addAction(snoozeAction) - .addAction(cancelAction) val builder = NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification) @@ -99,9 +99,9 @@ class AndroidNotificationTray .setContentText(habit.description) .setContentIntent(pendingIntents.showHabit(habit)) .setDeleteIntent(pendingIntents.dismissNotification(habit)) - .addAction(checkAction) + .addAction(addRepetitionAction) + .addAction(removeRepetitionAction) .addAction(snoozeAction) - .addAction(cancelAction) .setSound(ringtoneManager.getURI()) .extend(wearableExtender) .setWhen(reminderTime) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java index 9f98bd191..7074224e8 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java @@ -46,9 +46,6 @@ public class WidgetReceiver extends BroadcastReceiver public static final String ACTION_REMOVE_REPETITION = "org.isoron.uhabits.ACTION_REMOVE_REPETITION"; - public static final String ACTION_CANCEL_REPETITION = - "org.isoron.uhabits.ACTION_CANCEL_REPETITION"; - public static final String ACTION_TOGGLE_REPETITION = "org.isoron.uhabits.ACTION_TOGGLE_REPETITION"; @@ -91,11 +88,6 @@ public class WidgetReceiver extends BroadcastReceiver controller.onRemoveRepetition(data.getHabit(), data.getTimestamp()); break; - - case ACTION_CANCEL_REPETITION: - controller.onCancelRepetition(data.getHabit(), - data.getTimestamp()); - break; } } catch (RuntimeException e) diff --git a/uhabits-android/src/main/res/values/strings.xml b/uhabits-android/src/main/res/values/strings.xml index 3ea1d2f76..bb89c2fab 100644 --- a/uhabits-android/src/main/res/values/strings.xml +++ b/uhabits-android/src/main/res/values/strings.xml @@ -217,4 +217,6 @@ e.g. Did you exercise today? Question Target + Yes + No \ No newline at end of file diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java index 4615a2487..d3aa800af 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java @@ -48,25 +48,20 @@ public class WidgetBehavior public void onAddRepetition(@NonNull Habit habit, Timestamp timestamp) { + notificationTray.cancel(habit); Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); if (rep != null) return; performToggle(habit, timestamp); - notificationTray.cancel(habit); } public void onRemoveRepetition(@NonNull Habit habit, Timestamp timestamp) { + notificationTray.cancel(habit); Repetition rep = habit.getRepetitions().getByTimestamp(timestamp); if (rep == null) return; performToggle(habit, timestamp); } - public void onCancelRepetition(@NonNull Habit habit, Timestamp timestamp) - { - onRemoveRepetition(habit, timestamp); - notificationTray.cancel(habit); - } - public void onToggleRepetition(@NonNull Habit habit, Timestamp timestamp) { performToggle(habit, timestamp); From 2a4a7c975f3e71be3519156653ae66be541122fb Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Tue, 14 Nov 2017 22:35:37 -0600 Subject: [PATCH 13/19] Add action to show reminder right now (for developers) --- .../habits/list/ListHabitsSelectionMenu.kt | 15 ++++++++++++++- .../src/main/res/menu/list_habits_selection.xml | 5 +++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsSelectionMenu.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsSelectionMenu.kt index 9493b651d..65c75cc01 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsSelectionMenu.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsSelectionMenu.kt @@ -25,7 +25,10 @@ import org.isoron.androidbase.activities.* import org.isoron.uhabits.* import org.isoron.uhabits.activities.habits.list.views.* import org.isoron.uhabits.core.commands.* +import org.isoron.uhabits.core.preferences.* +import org.isoron.uhabits.core.ui.* import org.isoron.uhabits.core.ui.screens.habits.list.* +import org.isoron.uhabits.core.utils.* import javax.inject.* @ActivityScope @@ -33,8 +36,10 @@ class ListHabitsSelectionMenu @Inject constructor( private val screen: ListHabitsScreen, private val listAdapter: HabitCardListAdapter, var commandRunner: CommandRunner, + private val prefs: Preferences, private val behavior: ListHabitsSelectionMenuBehavior, - private val listController: Lazy + private val listController: Lazy, + private val notificationTray: NotificationTray ) : BaseSelectionMenu() { override fun onFinish() { @@ -69,6 +74,12 @@ class ListHabitsSelectionMenu @Inject constructor( return true } + R.id.action_notify -> { + for(h in listAdapter.selected) + notificationTray.show(h, DateUtils.getToday(), 0) + return true + } + else -> return false } } @@ -78,12 +89,14 @@ class ListHabitsSelectionMenu @Inject constructor( val itemColor = menu.findItem(R.id.action_color) val itemArchive = menu.findItem(R.id.action_archive_habit) val itemUnarchive = menu.findItem(R.id.action_unarchive_habit) + val itemNotify = menu.findItem(R.id.action_notify) itemColor.isVisible = true itemEdit.isVisible = behavior.canEdit() itemArchive.isVisible = behavior.canArchive() itemUnarchive.isVisible = behavior.canUnarchive() setTitle(Integer.toString(listAdapter.selected.size)) + itemNotify.isVisible = prefs.isDeveloper return true } diff --git a/uhabits-android/src/main/res/menu/list_habits_selection.xml b/uhabits-android/src/main/res/menu/list_habits_selection.xml index 4f274e39e..0e5cd850a 100644 --- a/uhabits-android/src/main/res/menu/list_habits_selection.xml +++ b/uhabits-android/src/main/res/menu/list_habits_selection.xml @@ -46,4 +46,9 @@ android:title="@string/delete" app:showAsAction="never"/> + + \ No newline at end of file From 0077d35ff9110aa0bc5ea5b3ad2777cc13a07e47 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Tue, 14 Nov 2017 23:03:46 -0600 Subject: [PATCH 14/19] Automatically dismiss summary notification --- .../notifications/AndroidNotificationTray.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index 15abc403f..f41c18bca 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -44,8 +44,15 @@ class AndroidNotificationTray private val ringtoneManager: RingtoneManager ) : NotificationTray.SystemTray { + private var active = HashSet() + override fun removeNotification(id: Int) { - NotificationManagerCompat.from(context).cancel(id) + val manager = NotificationManagerCompat.from(context) + manager.cancel(id) + active.remove(id) + + // Clear the group summary notification + if(active.isEmpty()) manager.cancelAll() } override fun showNotification(habit: Habit, @@ -58,6 +65,7 @@ class AndroidNotificationTray notificationManager.notify(Int.MAX_VALUE, summary) val notification = buildNotification(habit, reminderTime, timestamp) notificationManager.notify(notificationId, notification) + active.add(notificationId) } @NonNull @@ -79,8 +87,7 @@ class AndroidNotificationTray val removeRepetitionAction = Action( R.drawable.ic_action_cancel, context.getString(R.string.no), - pendingIntents.removeRepetition(habit) - ) + pendingIntents.removeRepetition(habit)) val wearableBg = decodeResource(context.resources, R.drawable.stripe) From 10be875b48533f2062f22b08c2d9eba728c0745e Mon Sep 17 00:00:00 2001 From: derebaba Date: Wed, 15 Nov 2017 10:57:00 +0300 Subject: [PATCH 15/19] Fixed test --- .../core/ui/screens/habits/show/ShowHabitMenuBehaviorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehaviorTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehaviorTest.java index 08aa1a7ea..fefade28e 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehaviorTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehaviorTest.java @@ -50,7 +50,7 @@ public class ShowHabitMenuBehaviorTest extends BaseUnitTest habit = fixtures.createShortHabit(); menu = new ShowHabitMenuBehavior(habitList, habit, taskRunner, screen, - system); + system, commandRunner); } @Test From 0dc46d02a458a4b96833fbc16a911636b53c8fe9 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 15 Nov 2017 21:54:30 -0600 Subject: [PATCH 16/19] Minor style changes --- .../activities/habits/show/ShowHabitScreen.java | 12 ++++++------ .../screens/habits/show/ShowHabitMenuBehavior.java | 10 ++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java index a929d25b9..c74b412da 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java @@ -26,8 +26,8 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.activities.common.dialogs.*; import org.isoron.uhabits.activities.habits.edit.*; import org.isoron.uhabits.core.models.*; +import org.isoron.uhabits.core.ui.callbacks.*; import org.isoron.uhabits.core.ui.screens.habits.show.*; -import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback; import javax.inject.*; @@ -56,19 +56,18 @@ public class ShowHabitScreen extends BaseScreen @NonNull Habit habit, @NonNull ShowHabitRootView view, @NonNull ShowHabitsMenu menu, - @NonNull - EditHabitDialogFactory editHabitDialogFactory, + @NonNull EditHabitDialogFactory editHabitDialogFactory, @NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory, @NonNull Lazy behavior) { super(activity); - this.confirmDeleteDialogFactory = confirmDeleteDialogFactory; setMenu(menu); setRootView(view); this.habit = habit; this.behavior = behavior; this.editHabitDialogFactory = editHabitDialogFactory; + this.confirmDeleteDialogFactory = confirmDeleteDialogFactory; view.setController(this); } @@ -122,18 +121,19 @@ public class ShowHabitScreen extends BaseScreen { case COULD_NOT_EXPORT: showMessage(R.string.could_not_export); + case HABIT_DELETED: showMessage(R.string.delete_habits_message); } } @Override - public void showDeleteConfirmationScreen(OnConfirmedCallback callback) { + public void showDeleteConfirmationScreen(@NonNull OnConfirmedCallback callback) { activity.showDialog(confirmDeleteDialogFactory.create(callback)); } @Override - public void endActivity() { + public void close() { activity.finish(); } } diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java index 4a70c7b79..ad426f528 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java @@ -21,11 +21,10 @@ package org.isoron.uhabits.core.ui.screens.habits.show; import android.support.annotation.*; -import org.isoron.uhabits.core.commands.CommandRunner; -import org.isoron.uhabits.core.commands.DeleteHabitsCommand; +import org.isoron.uhabits.core.commands.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.tasks.*; -import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback; +import org.isoron.uhabits.core.ui.callbacks.*; import java.io.*; import java.util.*; @@ -92,8 +91,7 @@ public class ShowHabitMenuBehavior screen.showDeleteConfirmationScreen(() -> { commandRunner.execute(new DeleteHabitsCommand(habitList, selected), null); - screen.showMessage(Message.HABIT_DELETED); - screen.endActivity(); + screen.close(); }); } @@ -113,7 +111,7 @@ public class ShowHabitMenuBehavior void showDeleteConfirmationScreen( @NonNull OnConfirmedCallback callback); - void endActivity(); + void close(); } public interface System From 7f257e045be74a4afb5fc8b4968de765b69d90d2 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Mon, 4 Dec 2017 23:13:31 -0600 Subject: [PATCH 17/19] MemoryHabitList: iterate over a copy of the list --- .../org/isoron/uhabits/core/models/memory/MemoryHabitList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.java index c1d05f517..fe54af3ec 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.java @@ -164,7 +164,7 @@ public class MemoryHabitList extends HabitList @Override public synchronized Iterator iterator() { - return Collections.unmodifiableCollection(list).iterator(); + return new ArrayList<>(list).iterator(); } @Override From 357f51fd5ee5e1cfb13d5f9ea764c1b918deb73e Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 6 Dec 2017 21:41:42 -0600 Subject: [PATCH 18/19] SnoozeDelayPickerActivity: Fix timepicker layout --- .../SnoozeDelayPickerActivity.java | 66 +++++++++---------- .../uhabits/receivers/ReminderController.java | 5 +- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java index ab51a7983..03d26a707 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java @@ -1,41 +1,31 @@ package org.isoron.uhabits.notifications; + +import android.app.*; import android.os.*; import android.support.annotation.*; import android.support.v4.app.*; -import android.support.v7.app.*; import android.text.format.*; import android.view.*; import android.widget.*; -import com.android.datetimepicker.time.*; +import com.android.datetimepicker.time.TimePickerDialog; import org.isoron.uhabits.*; -import org.isoron.uhabits.R; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.receivers.*; import java.util.*; -import butterknife.*; - -import static android.content.ContentUris.*; +import static android.content.ContentUris.parseId; -public class SnoozeDelayPickerActivity extends AppCompatActivity implements AdapterView - .OnItemClickListener +public class SnoozeDelayPickerActivity extends FragmentActivity + implements AdapterView.OnItemClickListener { - public static final String ACTION_ASK_SNOOZE = "org.isoron.uhabits.ACTION_ASK_SNOOZE"; - private Habit habit; private ReminderController reminderController; - @BindArray(R.array.snooze_picker_names) - protected String[] snoozeNames; - - @BindArray(R.array.snooze_picker_values) - protected int[] snoozeValues; - @Override protected void onCreate(@Nullable Bundle bundle) { @@ -49,19 +39,35 @@ public class SnoozeDelayPickerActivity extends AppCompatActivity implements Adap habit = appComponent.getHabitList().getById(parseId(getIntent().getData())); if (habit == null) finish(); - setTheme(R.style.Theme_AppCompat_Light_Dialog_Alert); - ButterKnife.bind(this); + int theme = R.style.Theme_AppCompat_Light_Dialog_Alert; + AlertDialog dialog = new AlertDialog.Builder(new ContextThemeWrapper(this, theme)) + .setTitle(R.string.select_snooze_delay) + .setItems(R.array.snooze_picker_names, null) + .create(); - ListView listView = new ListView(this); - listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, - snoozeNames)); - listView.setOnItemClickListener(this); - setContentView(listView); + dialog.getListView().setOnItemClickListener(this); + dialog.setOnDismissListener(d -> finish()); + dialog.show(); + } + + private void showTimePicker() + { + final Calendar calendar = Calendar.getInstance(); + TimePickerDialog dialog = TimePickerDialog.newInstance( + (view, hour, minute) -> { + reminderController.onSnoozeTimePicked(habit, hour, minute); + finish(); + }, + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + DateFormat.is24HourFormat(this)); + dialog.show(getSupportFragmentManager(), "timePicker"); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { + int[] snoozeValues = getResources().getIntArray(R.array.snooze_picker_values); if (snoozeValues[position] >= 0) { reminderController.onSnoozeDelayPicked(habit, snoozeValues[position]); @@ -70,16 +76,10 @@ public class SnoozeDelayPickerActivity extends AppCompatActivity implements Adap else showTimePicker(); } - private void showTimePicker() + @Override + public void finish() { - final Calendar calendar = Calendar.getInstance(); - int defaultHour = calendar.get(Calendar.HOUR_OF_DAY); - int defaultMinute = calendar.get(Calendar.MINUTE); - TimePickerDialog dialog = TimePickerDialog.newInstance( - (view, hour, minute) -> - reminderController.onSnoozeTimePicked(habit, hour, minute), - defaultHour, defaultMinute, DateFormat.is24HourFormat(this)); - - dialog.show(getSupportFragmentManager(), "timePicker"); + super.finish(); + overridePendingTransition(0, 0); } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java index 0d07a1212..c253064f1 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java @@ -104,9 +104,8 @@ public class ReminderController private void showSnoozeDelayPicker(@NonNull Habit habit, Context context) { context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - Intent intent = new Intent(SnoozeDelayPickerActivity.ACTION_ASK_SNOOZE, - Uri.parse(habit.getUriString()), - context, SnoozeDelayPickerActivity.class); + Intent intent = new Intent(context, SnoozeDelayPickerActivity.class); + intent.setData(Uri.parse(habit.getUriString())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } From bf8c14fc03dfa80730aa1414eb389b6846edad17 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 6 Dec 2017 22:23:28 -0600 Subject: [PATCH 19/19] Fix reminder tests --- .../isoron/uhabits/receivers/ReminderControllerTest.java | 2 +- .../isoron/uhabits/core/reminders/ReminderScheduler.java | 6 ++++-- .../uhabits/core/reminders/ReminderSchedulerTest.java | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java index 69528118a..b17bbace5 100644 --- a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java +++ b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.java @@ -72,7 +72,7 @@ public class ReminderControllerTest extends BaseAndroidJVMTest controller.onSnoozePressed(habit,null); - verify(reminderScheduler).scheduleAtTime(habit, nowTz + 900000); + verify(reminderScheduler).scheduleMinutesFromNow(habit, 15L); verify(notificationTray).cancel(habit); } diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java index ce4059ae8..51423aac7 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java @@ -52,19 +52,21 @@ public class ReminderScheduler implements CommandRunner.Listener public void onCommandExecuted(@NonNull Command command, @Nullable Long refreshKey) { - if(command instanceof ToggleRepetitionCommand) return; - if(command instanceof ChangeHabitColorCommand) return; + if (command instanceof ToggleRepetitionCommand) return; + if (command instanceof ChangeHabitColorCommand) return; scheduleAll(); } public void schedule(@NonNull Habit habit) { + if (!habit.hasReminder()) return; Long reminderTime = habit.getReminder().getTimeInMillis(); scheduleAtTime(habit, reminderTime); } public void scheduleAtTime(@NonNull Habit habit, @NonNull Long reminderTime) { + if (reminderTime == null) throw new IllegalArgumentException(); if (!habit.hasReminder()) return; if (habit.isArchived()) return; long timestamp = getStartOfDay(removeTimezone(reminderTime)); diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java index 480471670..82e147e16 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java @@ -118,7 +118,7 @@ public class ReminderSchedulerTest extends BaseUnitTest @Test public void testSchedule_withoutReminder() { - reminderScheduler.schedule(habit, null); + reminderScheduler.schedule(habit); Mockito.verifyZeroInteractions(sys); } @@ -133,7 +133,8 @@ public class ReminderSchedulerTest extends BaseUnitTest long expectedCheckmarkTime, long expectedReminderTime) { - reminderScheduler.schedule(habit, atTime); + if(atTime == null) reminderScheduler.schedule(habit); + else reminderScheduler.scheduleAtTime(habit, atTime); verify(sys).scheduleShowReminder(expectedReminderTime, habit, expectedCheckmarkTime); }