diff --git a/app/src/main/java/eu/faircode/email/ApplicationEx.java b/app/src/main/java/eu/faircode/email/ApplicationEx.java index 27a50a98d0..8740a19958 100644 --- a/app/src/main/java/eu/faircode/email/ApplicationEx.java +++ b/app/src/main/java/eu/faircode/email/ApplicationEx.java @@ -281,6 +281,9 @@ public class ApplicationEx extends Application case "schedule": case "schedule_start": case "schedule_end": + case "schedule_start_weekend": + case "schedule_end_weekend": + case "weekend": case "schedule_day0": case "schedule_day1": case "schedule_day2": diff --git a/app/src/main/java/eu/faircode/email/CalendarHelper.java b/app/src/main/java/eu/faircode/email/CalendarHelper.java index 51bbbde5d9..ca8368ff66 100644 --- a/app/src/main/java/eu/faircode/email/CalendarHelper.java +++ b/app/src/main/java/eu/faircode/email/CalendarHelper.java @@ -23,11 +23,16 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; +import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.provider.CalendarContract; import android.text.TextUtils; +import androidx.preference.PreferenceManager; + +import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.TimeZone; @@ -43,6 +48,28 @@ import biweekly.property.RecurrenceRule; import biweekly.util.ICalDate; public class CalendarHelper { + static boolean isWeekend(Context context, Calendar calendar) { + return isWeekend(context, calendar.get(Calendar.DAY_OF_WEEK)); + } + + static boolean isWeekend(Context context, int aday) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String weekend = prefs.getString("weekend", Calendar.SATURDAY + "," + Calendar.SUNDAY); + for (String day : weekend.split(",")) + if (aday == Integer.parseInt(day)) + return true; + return false; + } + + static String formatHour(Context context, int minutes) { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, minutes / 60); + cal.set(Calendar.MINUTE, minutes % 60); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return Helper.getTimeInstance(context, SimpleDateFormat.SHORT).format(cal.getTime()); + } + static void insert(Context context, ICalendar icalendar, VEvent event, String selectedAccount, String selectedName, EntityMessage message) { diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java b/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java index 9c7ec46861..c37af668c0 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java @@ -22,8 +22,10 @@ package eu.faircode.email; import android.app.Dialog; import android.app.TimePickerDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.text.format.DateFormat; @@ -44,6 +46,7 @@ import android.widget.TimePicker; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.constraintlayout.widget.Group; import androidx.fragment.app.DialogFragment; @@ -56,8 +59,8 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.text.DateFormatSymbols; -import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Objects; @@ -75,6 +78,9 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr private TextView tvSchedulePro; private TextView tvScheduleStart; private TextView tvScheduleEnd; + private TextView tvScheduleStartWeekend; + private TextView tvScheduleEndWeekend; + private ImageButton ibWeekend; private CheckBox[] cbDay; private TextView tvScheduleIgnore; private ImageButton ibSchedules; @@ -114,8 +120,12 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr private AdapterAccountExempted adapter; + private int textColorTertiary; + private int colorAccent; + private final static String[] RESET_OPTIONS = new String[]{ - "enabled", "poll_interval", "auto_optimize", "schedule", "schedule_start", "schedule_end", + "enabled", "poll_interval", "auto_optimize", + "schedule", "schedule_start", "schedule_end", "schedule_start_weekend", "schedule_end_weekend", "weekend", "sync_quick_imap", "sync_quick_pop", "sync_nodate", "sync_unseen", "sync_flagged", "delete_unseen", "sync_kept", "gmail_thread_id", "outlook_thread_id", "subject_threading", @@ -125,6 +135,15 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr "tune_keep_alive" }; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Context context = getContext(); + this.textColorTertiary = Helper.resolveColor(context, android.R.attr.textColorTertiary); + this.colorAccent = Helper.resolveColor(context, R.attr.colorAccent); + } + @Override @Nullable public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -147,6 +166,9 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr tvSchedulePro = view.findViewById(R.id.tvSchedulePro); tvScheduleStart = view.findViewById(R.id.tvScheduleStart); tvScheduleEnd = view.findViewById(R.id.tvScheduleEnd); + tvScheduleStartWeekend = view.findViewById(R.id.tvScheduleStartWeekend); + tvScheduleEndWeekend = view.findViewById(R.id.tvScheduleEndWeekend); + ibWeekend = view.findViewById(R.id.ibWeekend); cbDay = new CheckBox[]{ view.findViewById(R.id.cbDay0), view.findViewById(R.id.cbDay1), @@ -268,27 +290,24 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr Helper.linkPro(tvSchedulePro); - tvScheduleStart.setOnClickListener(new View.OnClickListener() { + View.OnClickListener onSchedule = new View.OnClickListener() { @Override public void onClick(View v) { - Bundle args = new Bundle(); - args.putBoolean("start", true); - DialogFragment timePicker = new TimePickerFragment(); - timePicker.setArguments(args); - timePicker.show(getParentFragmentManager(), "timePicker"); - } - }); + int id = v.getId(); - tvScheduleEnd.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { Bundle args = new Bundle(); - args.putBoolean("start", false); + args.putBoolean("start", id == R.id.tvScheduleStart || id == R.id.tvScheduleStartWeekend); + args.putBoolean("weekend", id == R.id.tvScheduleStartWeekend || id == R.id.tvScheduleEndWeekend); DialogFragment timePicker = new TimePickerFragment(); timePicker.setArguments(args); timePicker.show(getParentFragmentManager(), "timePicker"); } - }); + }; + + tvScheduleStart.setOnClickListener(onSchedule); + tvScheduleEnd.setOnClickListener(onSchedule); + tvScheduleStartWeekend.setOnClickListener(onSchedule); + tvScheduleEndWeekend.setOnClickListener(onSchedule); String[] daynames = new DateFormatSymbols().getWeekdays(); for (int i = 0; i < 7; i++) { @@ -302,6 +321,14 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr }); } + ibWeekend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FragmentDialogWeekend fragment = new FragmentDialogWeekend(); + fragment.show(getParentFragmentManager(), "weekend"); + } + }); + tvScheduleIgnore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -571,13 +598,14 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr if (view == null || getContext() == null) return; - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean pro = ActivityBilling.isPro(getContext()); + final Context context = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + boolean pro = ActivityBilling.isPro(context); swEnabled.setChecked(prefs.getBoolean("enabled", true)); swOptimize.setChecked(prefs.getBoolean("auto_optimize", false)); - int pollInterval = ServiceSynchronize.getPollInterval(getContext()); + int pollInterval = ServiceSynchronize.getPollInterval(context); int[] pollIntervalValues = getResources().getIntArray(R.array.pollIntervalValues); for (int pos = 0; pos < pollIntervalValues.length; pos++) if (pollIntervalValues[pos] == pollInterval) { @@ -591,10 +619,22 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr swSchedule.setChecked(prefs.getBoolean("schedule", false) && pro); swSchedule.setEnabled(pro); - tvScheduleStart.setText(formatHour(getContext(), prefs.getInt("schedule_start", 0))); - tvScheduleEnd.setText(formatHour(getContext(), prefs.getInt("schedule_end", 0))); - for (int i = 0; i < 7; i++) + + int schedule_start = prefs.getInt("schedule_start", 0); + int schedule_end = prefs.getInt("schedule_end", 0); + int schedule_start_weekend = prefs.getInt("schedule_start_weekend", schedule_start); + int schedule_end_weekend = prefs.getInt("schedule_end_weekend", schedule_end); + tvScheduleStart.setText(CalendarHelper.formatHour(context, schedule_start)); + tvScheduleEnd.setText(CalendarHelper.formatHour(context, schedule_end)); + tvScheduleStartWeekend.setText(CalendarHelper.formatHour(context, schedule_start_weekend)); + tvScheduleEndWeekend.setText(CalendarHelper.formatHour(context, schedule_end_weekend)); + + for (int i = 0; i < 7; i++) { + boolean weekend = CalendarHelper.isWeekend(context, i + 1); + cbDay[i].setTypeface(weekend ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT); + cbDay[i].setTextColor(weekend ? colorAccent : textColorTertiary); cbDay[i].setChecked(prefs.getBoolean("schedule_day" + i, true)); + } swQuickSyncImap.setChecked(prefs.getBoolean("sync_quick_imap", false)); swQuickSyncPop.setChecked(prefs.getBoolean("sync_quick_pop", true)); @@ -628,24 +668,13 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr rvBlocklist.setAlpha(swCheckBlocklist.isChecked() ? 1.0f : Helper.LOW_LIGHT); } - private String formatHour(Context context, int minutes) { - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.HOUR_OF_DAY, minutes / 60); - cal.set(Calendar.MINUTE, minutes % 60); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - return Helper.getTimeInstance(context, SimpleDateFormat.SHORT).format(cal.getTime()); - } - public static class TimePickerFragment extends FragmentDialogBase implements TimePickerDialog.OnTimeSetListener { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - Bundle args = getArguments(); - boolean start = args.getBoolean("start"); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - int minutes = prefs.getInt("schedule_" + (start ? "start" : "end"), 0); + final Context context = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + int minutes = prefs.getInt(getKey(), 0); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, minutes / 60); @@ -653,21 +682,58 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); - return new TimePickerDialog(getContext(), this, + return new TimePickerDialog(context, this, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), - DateFormat.is24HourFormat(getContext())); + DateFormat.is24HourFormat(context)); } public void onTimeSet(TimePicker view, int hour, int minute) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(getKey(), hour * 60 + minute).putBoolean("schedule", true).apply(); + } + + private String getKey() { Bundle args = getArguments(); boolean start = args.getBoolean("start"); + boolean weekend = args.getBoolean("weekend"); + return "schedule" + (start ? "_start" : "_end") + (weekend ? "_weekend" : ""); + } + } - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - SharedPreferences.Editor editor = prefs.edit(); - editor.putInt("schedule_" + (start ? "start" : "end"), hour * 60 + minute); - editor.putBoolean("schedule", true); - editor.apply(); + public static class FragmentDialogWeekend extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + boolean[] days = new boolean[7]; + + final Context context = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + String[] daynames = Arrays.copyOfRange(new DateFormatSymbols().getWeekdays(), 1, 8); + + String weekend = prefs.getString("weekend", Calendar.SATURDAY + "," + Calendar.SUNDAY); + for (String day : weekend.split(",")) + days[Integer.parseInt(day) - 1] = true; + + return new AlertDialog.Builder(context) + .setTitle(R.string.title_advanced_schedule_weekend) + .setMultiChoiceItems(daynames, days, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < days.length; i++) + if (days[i]) { + if (sb.length() > 0) + sb.append(","); + sb.append(i + 1); + } + prefs.edit().putString("weekend", sb.toString()).apply(); + } + }) + .setNegativeButton(R.string.title_setup_done, null) + .create(); } } diff --git a/app/src/main/java/eu/faircode/email/Log.java b/app/src/main/java/eu/faircode/email/Log.java index a80bad1459..011c3e2ec9 100644 --- a/app/src/main/java/eu/faircode/email/Log.java +++ b/app/src/main/java/eu/faircode/email/Log.java @@ -2396,15 +2396,21 @@ public class Log { if (schedule) { int minuteStart = prefs.getInt("schedule_start", 0); int minuteEnd = prefs.getInt("schedule_end", 0); + int minuteStartWeekend = prefs.getInt("schedule_start_weekend", 0); + int minuteEndWeekend = prefs.getInt("schedule_end_weekend", 0); - size += write(os, "schedule " + - (minuteStart / 60) + ":" + (minuteStart % 60) + "..." + - (minuteEnd / 60) + ":" + (minuteEnd % 60) + "\r\n"); + size += write(os, String.format("schedule %s...%s weekend %s...%s\r\n", + CalendarHelper.formatHour(context, minuteStart), + CalendarHelper.formatHour(context, minuteEnd), + CalendarHelper.formatHour(context, minuteStartWeekend), + CalendarHelper.formatHour(context, minuteEndWeekend))); String[] daynames = new DateFormatSymbols().getWeekdays(); for (int i = 0; i < 7; i++) { boolean day = prefs.getBoolean("schedule_day" + i, true); - size += write(os, "schedule " + daynames[i + 1] + "=" + day + "\r\n"); + boolean weekend = CalendarHelper.isWeekend(context, i + 1); + size += write(os, String.format("schedule %s=%b %s\r\n", + daynames[i + 1], day, weekend ? "weekend" : "")); } size += write(os, "\r\n"); diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 5d922a9f04..171f273c7d 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -3293,10 +3293,14 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences if (!ActivityBilling.isPro(context)) return null; - int minuteStart = prefs.getInt("schedule_start", 0); - int minuteEnd = prefs.getInt("schedule_end", 0); - Calendar calStart = Calendar.getInstance(); + boolean weekend = CalendarHelper.isWeekend(context, calStart); + int defStart = (weekend ? prefs.getInt("schedule_start", 0) : 0); + int defEnd = (weekend ? prefs.getInt("schedule_end", 0) : 0); + + int minuteStart = prefs.getInt("schedule_start" + (weekend ? "_weekend" : ""), defStart); + int minuteEnd = prefs.getInt("schedule_end" + (weekend ? "_weekend" : ""), defEnd); + calStart.set(Calendar.HOUR_OF_DAY, minuteStart / 60); calStart.set(Calendar.MINUTE, minuteStart % 60); calStart.set(Calendar.SECOND, 0); @@ -3320,7 +3324,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences boolean son = prefs.getBoolean("schedule_day" + sdow, true); boolean eon = prefs.getBoolean("schedule_day" + edow, true); - if (BuildConfig.DEBUG) + if (BuildConfig.DEBUG && false) Log.i("@@@ eval dow=" + sdow + "/" + edow + " on=" + son + "/" + eon + " start=" + new Date(calStart.getTimeInMillis()) + @@ -3342,6 +3346,12 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences calEnd.set(Calendar.MINUTE, 0); } + if (BuildConfig.DEBUG) + Log.i("@@@ eval dow=" + sdow + "/" + edow + + " on=" + son + "/" + eon + + " start=" + new Date(calStart.getTimeInMillis()) + + " end=" + new Date(calEnd.getTimeInMillis())); + break; } diff --git a/app/src/main/res/layout/fragment_options_synchronize.xml b/app/src/main/res/layout/fragment_options_synchronize.xml index d25708b2f1..46fa7e5893 100644 --- a/app/src/main/res/layout/fragment_options_synchronize.xml +++ b/app/src/main/res/layout/fragment_options_synchronize.xml @@ -247,6 +247,17 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/swSchedule" /> + + + app:layout_constraintTop_toBottomOf="@id/tvScheduleWorkDays" /> + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/tvScheduleStartWeekend" /> @@ -306,6 +374,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Monday" + android:textAppearance="@style/TextAppearance.AppCompat.Small" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbDay0" /> @@ -314,6 +383,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tuesday" + android:textAppearance="@style/TextAppearance.AppCompat.Small" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbDay1" /> @@ -322,6 +392,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Wednesday" + android:textAppearance="@style/TextAppearance.AppCompat.Small" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbDay2" /> @@ -330,6 +401,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Thursday" + android:textAppearance="@style/TextAppearance.AppCompat.Small" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbDay3" /> @@ -338,6 +410,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Friday" + android:textAppearance="@style/TextAppearance.AppCompat.Small" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbDay4" /> @@ -346,6 +419,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Saturday" + android:textAppearance="@style/TextAppearance.AppCompat.Small" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbDay5" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4ef3e8a0c..813d5701fe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -402,6 +402,8 @@ Automatically optimize Always receive messages for these accounts Schedule + Workdays + Weekend Advanced Quick sync Messages without date