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 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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();