Repository: Futsch1/medTimer (language: kotlin)

Issue:
Weekend notification delay
It should be possible to configure an "earliest notification time" for weekends or configurable days. This setting will result in all earlier reminders for that day to be scheduled at that time (like a "do not disturb" setting).


Discussion hints:
nan

Pull request: Weekend mode
fixes #63 

Test command: ./gradlew :app:testDebugUnitTest --tests "com.futsch1.medtimer.WeekendModeTest"

Fix patch:
diff --git a/app/src/main/java/com/futsch1/medtimer/PreferencesActivity.java b/app/src/main/java/com/futsch1/medtimer/PreferencesActivity.java
index 701f5cbed..584e2f9eb 100644
--- a/app/src/main/java/com/futsch1/medtimer/PreferencesActivity.java
+++ b/app/src/main/java/com/futsch1/medtimer/PreferencesActivity.java
@@ -27,7 +27,11 @@ protected void onCreate(Bundle savedInstanceState) {
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         if (item.getItemId() == android.R.id.home) {
-            finish();
+            if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
+                getSupportFragmentManager().popBackStack();
+            } else {
+                finish();
+            }
             return true;
         }
         return super.onOptionsItemSelected(item);
diff --git a/app/src/main/java/com/futsch1/medtimer/WeekendModePreferencesFragment.java b/app/src/main/java/com/futsch1/medtimer/WeekendModePreferencesFragment.java
new file mode 100644
index 000000000..da1c1ad56
--- /dev/null
+++ b/app/src/main/java/com/futsch1/medtimer/WeekendModePreferencesFragment.java
@@ -0,0 +1,77 @@
+package com.futsch1.medtimer;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+import androidx.preference.MultiSelectListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+
+import com.futsch1.medtimer.helpers.TimeHelper;
+import com.futsch1.medtimer.reminders.ReminderProcessor;
+import com.takisoft.preferencex.PreferenceFragmentCompat;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class WeekendModePreferencesFragment extends PreferenceFragmentCompat {
+
+    public static final String WEEKEND_TIME = "weekend_time";
+    public static final String WEEKEND_MODE = "weekend_mode";
+    public static final String WEEKEND_DAYS = "weekend_days";
+
+    @Override
+    public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) {
+        setPreferencesFromResource(R.xml.weekend_mode_preferences, rootKey);
+
+        setupWeekendMode();
+        setupTimePicker();
+        setupDays();
+    }
+
+    private void setupWeekendMode() {
+        Preference preference = getPreferenceScreen().findPreference(WEEKEND_MODE);
+        if (preference != null) {
+            preference.setOnPreferenceChangeListener((preference1, newValue) -> {
+                requestReschedule();
+                return true;
+            });
+        }
+    }
+
+    private void setupTimePicker() {
+        Preference preference = getPreferenceScreen().findPreference(WEEKEND_TIME);
+        if (preference != null) {
+            SharedPreferences defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
+            preference.setSummary(TimeHelper.minutesToTime(defaultSharedPreferences.getInt(WEEKEND_TIME, 540)));
+            preference.setOnPreferenceClickListener(preference1 -> {
+                int weekendTime = defaultSharedPreferences.getInt(WEEKEND_TIME, 540);
+                new TimeHelper.TimePickerWrapper(requireContext()).show(weekendTime / 60, weekendTime % 60, minutes -> {
+                    defaultSharedPreferences.edit().putInt(WEEKEND_TIME, minutes).apply();
+                    preference1.setSummary(TimeHelper.minutesToTime(minutes));
+                    requestReschedule();
+                });
+                return true;
+            });
+        }
+    }
+
+    private void setupDays() {
+        Preference preference = getPreferenceScreen().findPreference(WEEKEND_DAYS);
+        if (preference != null) {
+            preference.setSummaryProvider((Preference.SummaryProvider<MultiSelectListPreference>) preference1 -> {
+                @SuppressWarnings("java:S6204") // Using SDK 33
+                List<CharSequence> values = preference1.getValues().stream().map(s -> preference1.getEntries()[Integer.parseInt(s) - 1]).collect(Collectors.toList());
+                return String.join(", ", values);
+            });
+            preference.setOnPreferenceChangeListener((preference12, newValue) -> {
+                requestReschedule();
+                return true;
+            });
+        }
+    }
+
+    private void requestReschedule() {
+        ReminderProcessor.requestReschedule(requireContext());
+    }
+}
diff --git a/app/src/main/java/com/futsch1/medtimer/helpers/TimeHelper.java b/app/src/main/java/com/futsch1/medtimer/helpers/TimeHelper.java
index 84ae901cc..ada99cde5 100644
--- a/app/src/main/java/com/futsch1/medtimer/helpers/TimeHelper.java
+++ b/app/src/main/java/com/futsch1/medtimer/helpers/TimeHelper.java
@@ -1,5 +1,9 @@
 package com.futsch1.medtimer.helpers;
 
+import android.app.TimePickerDialog;
+import android.content.Context;
+import android.text.format.DateFormat;
+
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
@@ -13,4 +17,21 @@ private TimeHelper() {
     public static String minutesToTime(long minutes) {
         return DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(LocalTime.of((int) (minutes / 60), (int) (minutes % 60)));
     }
+
+    public interface TimePickerResult {
+        void onTimeSelected(int minutes);
+    }
+
+    public static class TimePickerWrapper {
+        Context context;
+
+        public TimePickerWrapper(Context context) {
+            this.context = context;
+        }
+
+        public void show(int hourOfDay, int minute, TimePickerResult timePickerResult) {
+            TimePickerDialog timePickerDialog = new TimePickerDialog(context, (view, hourOfDayLocal, minuteLocal) -> timePickerResult.onTimeSelected(hourOfDayLocal * 60 + minuteLocal), hourOfDay, minute, DateFormat.is24HourFormat(context));
+            timePickerDialog.show();
+        }
+    }
 }
diff --git a/app/src/main/java/com/futsch1/medtimer/medicine/EditMedicine.java b/app/src/main/java/com/futsch1/medtimer/medicine/EditMedicine.java
index 99bf77f78..37d20b555 100644
--- a/app/src/main/java/com/futsch1/medtimer/medicine/EditMedicine.java
+++ b/app/src/main/java/com/futsch1/medtimer/medicine/EditMedicine.java
@@ -5,7 +5,6 @@
 import static com.futsch1.medtimer.ActivityCodes.EXTRA_MEDICINE_NAME;
 import static com.futsch1.medtimer.ActivityCodes.EXTRA_USE_COLOR;
 
-import android.app.TimePickerDialog;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.Color;
@@ -13,7 +12,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
-import android.text.format.DateFormat;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.EditText;
@@ -34,6 +32,7 @@
 import com.futsch1.medtimer.helpers.DeleteHelper;
 import com.futsch1.medtimer.helpers.DialogHelper;
 import com.futsch1.medtimer.helpers.SwipeHelper;
+import com.futsch1.medtimer.helpers.TimeHelper;
 import com.futsch1.medtimer.helpers.ViewColorHelper;
 import com.google.android.material.button.MaterialButton;
 import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
@@ -160,11 +159,10 @@ private void createReminder(String amount) {
         reminder.amount = amount;
         reminder.createdTimestamp = Instant.now().toEpochMilli() / 1000;
 
-        TimePickerDialog timePickerDialog = new TimePickerDialog(this, (tpView, hourOfDay, minute) -> {
-            reminder.timeInMinutes = hourOfDay * 60 + minute;
+        new TimeHelper.TimePickerWrapper(getApplicationContext()).show(0, 0, minutes -> {
+            reminder.timeInMinutes = minutes;
             medicineViewModel.insertReminder(reminder);
-        }, 8, 0, DateFormat.is24HourFormat(getApplicationContext()));
-        timePickerDialog.show();
+        });
     }
 
     @Override
diff --git a/app/src/main/java/com/futsch1/medtimer/medicine/ReminderViewHolder.java b/app/src/main/java/com/futsch1/medtimer/medicine/ReminderViewHolder.java
index e06026293..ec2da1b13 100644
--- a/app/src/main/java/com/futsch1/medtimer/medicine/ReminderViewHolder.java
+++ b/app/src/main/java/com/futsch1/medtimer/medicine/ReminderViewHolder.java
@@ -4,10 +4,8 @@
 
 import android.annotation.SuppressLint;
 import android.app.AlertDialog;
-import android.app.TimePickerDialog;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.text.format.DateFormat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -19,6 +17,7 @@
 
 import com.futsch1.medtimer.R;
 import com.futsch1.medtimer.database.Reminder;
+import com.futsch1.medtimer.helpers.TimeHelper;
 import com.google.android.material.button.MaterialButton;
 
 public class ReminderViewHolder extends RecyclerView.ViewHolder {
@@ -55,15 +54,13 @@ public void bind(Reminder reminder, DeleteCallback deleteCallback) {
         editTime.setText(minutesToTime(reminder.timeInMinutes));
         editDaysBetweenReminders.setText(Integer.toString(reminder.daysBetweenReminders));
 
-        TimePickerDialog timePickerDialog = new TimePickerDialog(editTime.getContext(), (view, hourOfDay, minute) -> {
-            String selectedTime = minutesToTime(hourOfDay * 60L + minute);
-            editTime.setText(selectedTime);
-            reminder.timeInMinutes = hourOfDay * 60 + minute;
-        }, 0, 0, DateFormat.is24HourFormat(editTime.getContext()));
         editTime.setOnFocusChangeListener((v, hasFocus) -> {
             if (hasFocus) {
-                timePickerDialog.updateTime(reminder.timeInMinutes / 60, reminder.timeInMinutes % 60);
-                timePickerDialog.show();
+                new TimeHelper.TimePickerWrapper(editTime.getContext()).show(reminder.timeInMinutes / 60, reminder.timeInMinutes % 60, minutes -> {
+                    String selectedTime = minutesToTime(minutes);
+                    editTime.setText(selectedTime);
+                    reminder.timeInMinutes = minutes;
+                });
             }
         });
 
diff --git a/app/src/main/java/com/futsch1/medtimer/reminders/RescheduleWork.java b/app/src/main/java/com/futsch1/medtimer/reminders/RescheduleWork.java
index 64a4bc978..143a91df6 100644
--- a/app/src/main/java/com/futsch1/medtimer/reminders/RescheduleWork.java
+++ b/app/src/main/java/com/futsch1/medtimer/reminders/RescheduleWork.java
@@ -34,10 +34,12 @@
 public class RescheduleWork extends Worker {
     protected final Context context;
     private final AlarmManager alarmManager;
+    private final WeekendMode weekendMode;
 
     public RescheduleWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
         super(context, workerParams);
         this.context = context;
+        this.weekendMode = new WeekendMode(PreferenceManager.getDefaultSharedPreferences(context));
         alarmManager = context.getSystemService(AlarmManager.class);
     }
 
@@ -66,6 +68,8 @@ public LocalDate localDate() {
     }
 
     protected void schedule(Instant timestamp, int reminderId, String medicineName, int requestCode, int reminderEventId) {
+        // Apply weekend mode shift
+        timestamp = weekendMode.adjustInstant(timestamp);
         // If the alarm is in the future, schedule with alarm manager
         if (timestamp.isAfter(Instant.now())) {
             PendingIntent pendingIntent = getPendingIntent(context, reminderId, requestCode, reminderEventId);
diff --git a/app/src/main/java/com/futsch1/medtimer/reminders/WeekendMode.java b/app/src/main/java/com/futsch1/medtimer/reminders/WeekendMode.java
new file mode 100644
index 000000000..114eda91d
--- /dev/null
+++ b/app/src/main/java/com/futsch1/medtimer/reminders/WeekendMode.java
@@ -0,0 +1,40 @@
+package com.futsch1.medtimer.reminders;
+
+import android.content.SharedPreferences;
+import android.util.ArraySet;
+
+import com.futsch1.medtimer.WeekendModePreferencesFragment;
+
+import java.time.DayOfWeek;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Set;
+
+public class WeekendMode {
+
+    private final SharedPreferences preferences;
+
+    public WeekendMode(SharedPreferences preferences) {
+        this.preferences = preferences;
+    }
+
+    public Instant adjustInstant(Instant instant) {
+        if (isWeekendModeEnabled()) {
+            int weekendTime = preferences.getInt(WeekendModePreferencesFragment.WEEKEND_TIME, 540);
+            Set<String> weekendDays = preferences.getStringSet(WeekendModePreferencesFragment.WEEKEND_DAYS, new ArraySet<>());
+            ZonedDateTime localDateTime = instant.atZone(ZoneId.systemDefault());
+            DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
+            int minutes = localDateTime.getMinute() + localDateTime.getHour() * 60;
+            int deltaMinutes = weekendTime - minutes;
+            if (weekendDays.contains(String.valueOf(dayOfWeek.getValue())) && deltaMinutes > 0) {
+                instant = instant.plusSeconds(deltaMinutes * 60L);
+            }
+        }
+        return instant;
+    }
+
+    private boolean isWeekendModeEnabled() {
+        return preferences.getBoolean(WeekendModePreferencesFragment.WEEKEND_MODE, false);
+    }
+}
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1dce31c5b..f08225588 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -92,4 +92,14 @@
     <string name="minutes_60">60 Minuten</string>
     <string name="notification_skipped">ÜBERSPRUNGEN</string>
     <string name="notification_snooze">SNOOZE</string>
+    <string name="weekend_mode">Wochenend-Modus</string>
+    <string name="weekend_mode_days">Tage</string>
+    <string name="monday">Montag</string>
+    <string name="tuesday">Dienstag</string>
+    <string name="wednesday">Mittwoch</string>
+    <string name="thursday">Donnerstag</string>
+    <string name="friday">Freitag</string>
+    <string name="saturday">Samstag</string>
+    <string name="sunday">Sonntag</string>
+    <string name="weekend_mode_summary">Verzögerung von Benachrichtigungen an bestimmten Wochentagen bis zu einer bestimmten Uhrzeit</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 576666089..97d8048f9 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -91,4 +91,14 @@
     <string name="minutes_60">60 minutos</string>
     <string name="notification_skipped">OMITIDO</string>
     <string name="notification_snooze">SNOOZE</string>
+    <string name="weekend_mode">Modo fin de semana</string>
+    <string name="weekend_mode_days">Días</string>
+    <string name="monday">Lunes</string>
+    <string name="tuesday">Martes</string>
+    <string name="wednesday">Miércoles</string>
+    <string name="thursday">Jueves</string>
+    <string name="friday">Viernes</string>
+    <string name="saturday">Sábado</string>
+    <string name="sunday">Domingo</string>
+    <string name="weekend_mode_summary">Retrasar las notificaciones de ciertos días de la semana hasta una hora determinada</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index af4d8e5e2..048ea7230 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -54,4 +54,26 @@
         <item>30</item>
         <item>60</item>
     </string-array>
+    <string-array name="days">
+        <item>@string/monday</item>
+        <item>@string/tuesday</item>
+        <item>@string/wednesday</item>
+        <item>@string/thursday</item>
+        <item>@string/friday</item>
+        <item>@string/saturday</item>
+        <item>@string/sunday</item>
+    </string-array>
+    <string-array name="one_to_seven">
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+        <item>4</item>
+        <item>5</item>
+        <item>6</item>
+        <item>7</item>
+    </string-array>
+    <string-array name="default_weekend_days">
+        <item>6</item>
+        <item>7</item>
+    </string-array>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fa4040c12..af96f20c6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -95,4 +95,14 @@
     <string name="minutes_60">60 minutes</string>
     <string name="notification_skipped">SKIPPED</string>
     <string name="notification_snooze">SNOOZE</string>
+    <string name="weekend_mode">Weekend mode</string>
+    <string name="weekend_mode_days">Days</string>
+    <string name="monday">Monday</string>
+    <string name="tuesday">Tuesday</string>
+    <string name="wednesday">Wednesday</string>
+    <string name="thursday">Thursday</string>
+    <string name="friday">Friday</string>
+    <string name="saturday">Saturday</string>
+    <string name="sunday">Sunday</string>
+    <string name="weekend_mode_summary">Delay notifications on certain days of the week until a given time</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index d9ddca94d..ceb6e68d8 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -34,6 +34,11 @@
         android:summary="@string/exact_reminders_summary"
         android:title="@string/exact_reminders"
         app:key="exact_reminders" />
+    <Preference
+        app:fragment="com.futsch1.medtimer.WeekendModePreferencesFragment"
+        app:key="weekend_mode"
+        app:summary="@string/weekend_mode_summary"
+        app:title="@string/weekend_mode" />
     <ListPreference
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/app/src/main/res/xml/weekend_mode_preferences.xml b/app/src/main/res/xml/weekend_mode_preferences.xml
new file mode 100644
index 000000000..3ffdc4a40
--- /dev/null
+++ b/app/src/main/res/xml/weekend_mode_preferences.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <SwitchPreferenceCompat
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:defaultValue="false"
+        app:key="weekend_mode"
+        app:title="@string/weekend_mode" />
+    <MultiSelectListPreference
+        app:defaultValue="@array/default_weekend_days"
+        app:entries="@array/days"
+        app:entryValues="@array/one_to_seven"
+        app:key="weekend_days"
+        app:title="@string/weekend_mode_days" />
+    <Preference
+        android:defaultValue="540"
+        app:key="weekend_time"
+        app:title="@string/time" />
+
+</androidx.preference.PreferenceScreen>
\ No newline at end of file


Test patch:
diff --git a/app/src/test/java/com/futsch1/medtimer/WeekendModeTest.java b/app/src/test/java/com/futsch1/medtimer/WeekendModeTest.java
new file mode 100644
index 000000000..2e86d4147
--- /dev/null
+++ b/app/src/test/java/com/futsch1/medtimer/WeekendModeTest.java
@@ -0,0 +1,118 @@
+package com.futsch1.medtimer;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.SharedPreferences;
+import android.util.ArraySet;
+
+import com.futsch1.medtimer.reminders.WeekendMode;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.robolectric.RobolectricTestRunner;
+
+import java.time.DayOfWeek;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(RobolectricTestRunner.class)
+public class WeekendModeTest {
+    @Test
+    public void test_adjustInstant_ReturnsSameInstant_WeekendModeDisabled() {
+        SharedPreferences preferences = Mockito.mock(SharedPreferences.class);
+        WeekendMode weekendMode = new WeekendMode(preferences);
+        Instant instant = Instant.now();
+
+        Mockito.when(preferences.getBoolean(eq(WeekendModePreferencesFragment.WEEKEND_MODE), anyBoolean())).thenReturn(false);
+
+        Instant adjustedInstant = weekendMode.adjustInstant(instant);
+
+        assertEquals(instant, adjustedInstant);
+    }
+
+    @Test
+    public void test_adjustInstant_ReturnsSameInstant_WeekendDaysEmpty() {
+        SharedPreferences preferences = Mockito.mock(SharedPreferences.class);
+        WeekendMode weekendMode = new WeekendMode(preferences);
+        Instant instant = Instant.now();
+
+        Mockito.when(preferences.getBoolean(WeekendModePreferencesFragment.WEEKEND_MODE, false)).thenReturn(true);
+        Mockito.when(preferences.getStringSet(WeekendModePreferencesFragment.WEEKEND_DAYS, new ArraySet<>())).thenReturn(new HashSet<>());
+
+        Instant adjustedInstant = weekendMode.adjustInstant(instant);
+
+        assertEquals(instant, adjustedInstant);
+    }
+
+    @Test
+    public void test_adjustInstant_ReturnsSameInstant_CurrentDayIsNotWeekend() {
+        SharedPreferences preferences = Mockito.mock(SharedPreferences.class);
+        Instant instant = Mockito.mock(Instant.class);
+        WeekendMode weekendMode = new WeekendMode(preferences);
+        ZonedDateTime testZonedDateTime = ZonedDateTime.of(2024, 4, 6, 8, 0, 0, 0, ZoneId.of("Z"));
+
+        Mockito.when(preferences.getBoolean(eq(WeekendModePreferencesFragment.WEEKEND_MODE), anyBoolean())).thenReturn(true);
+        Mockito.when(preferences.getInt(eq(WeekendModePreferencesFragment.WEEKEND_TIME), anyInt())).thenReturn(10 * 60);
+        Mockito.when(instant.atZone(any())).thenReturn(testZonedDateTime);
+        Set<String> weekendDays = new HashSet<>();
+        weekendDays.add(String.valueOf(DayOfWeek.TUESDAY.getValue()));
+        weekendDays.add(String.valueOf(DayOfWeek.WEDNESDAY.getValue()));
+        Mockito.when(preferences.getStringSet(WeekendModePreferencesFragment.WEEKEND_DAYS, new ArraySet<>())).thenReturn(weekendDays);
+
+        Instant adjustedInstant = weekendMode.adjustInstant(instant);
+
+        assertEquals(instant, adjustedInstant);
+    }
+
+    @Test
+    public void test_adjustInstant_ReturnsAdjustedInstant_WeekendModeEnabled_CurrentDayIsWeekend() {
+        SharedPreferences preferences = Mockito.mock(SharedPreferences.class);
+        Instant instant = Mockito.mock(Instant.class);
+        WeekendMode weekendMode = new WeekendMode(preferences);
+        ZonedDateTime testZonedDateTime = ZonedDateTime.of(2024, 4, 6, 8, 0, 0, 0, ZoneId.of("Z"));
+
+        Mockito.when(preferences.getBoolean(eq(WeekendModePreferencesFragment.WEEKEND_MODE), anyBoolean())).thenReturn(true);
+        Mockito.when(preferences.getInt(eq(WeekendModePreferencesFragment.WEEKEND_TIME), anyInt())).thenReturn(10 * 60);
+        Mockito.when(instant.atZone(any())).thenReturn(testZonedDateTime);
+        Set<String> weekendDays = new HashSet<>();
+        weekendDays.add(String.valueOf(DayOfWeek.SATURDAY.getValue()));
+        weekendDays.add(String.valueOf(DayOfWeek.SUNDAY.getValue()));
+        Mockito.when(preferences.getStringSet(WeekendModePreferencesFragment.WEEKEND_DAYS, new ArraySet<>())).thenReturn(weekendDays);
+
+        weekendMode.adjustInstant(instant);
+
+        verify(instant, times(1)).plusSeconds(2 * 60 * 60);
+    }
+
+    @Test
+    public void test_adjustInstant_ReturnsAdjustedInstant_WeekendModeEnabled_CurrentDayIsWeekendReminderIsLater() {
+        SharedPreferences preferences = Mockito.mock(SharedPreferences.class);
+        Instant instant = Mockito.mock(Instant.class);
+        WeekendMode weekendMode = new WeekendMode(preferences);
+        ZonedDateTime testZonedDateTime = ZonedDateTime.of(2024, 4, 6, 11, 0, 0, 0, ZoneId.of("Z"));
+
+        Mockito.when(preferences.getBoolean(eq(WeekendModePreferencesFragment.WEEKEND_MODE), anyBoolean())).thenReturn(true);
+        Mockito.when(preferences.getInt(eq(WeekendModePreferencesFragment.WEEKEND_TIME), anyInt())).thenReturn(10 * 60);
+        Mockito.when(instant.atZone(any())).thenReturn(testZonedDateTime);
+        Set<String> weekendDays = new HashSet<>();
+        weekendDays.add(String.valueOf(DayOfWeek.SATURDAY.getValue()));
+        weekendDays.add(String.valueOf(DayOfWeek.SUNDAY.getValue()));
+        Mockito.when(preferences.getStringSet(WeekendModePreferencesFragment.WEEKEND_DAYS, new ArraySet<>())).thenReturn(weekendDays);
+
+        weekendMode.adjustInstant(instant);
+
+        verify(instant, times(0)).plusSeconds(anyLong());
+    }
+}
+