diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java index d51d7848c..75ca2f7e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java @@ -50,6 +50,8 @@ public final class SettingsValues extends SignalStoreValues { public static final String PREFER_SYSTEM_EMOJI = "settings.use.system.emoji"; public static final String ENTER_KEY_SENDS = "settings.enter.key.sends"; public static final String BACKUPS_ENABLED = "settings.backups.enabled"; + public static final String BACKUPS_SCHEDULE_HOUR = "settings.backups.schedule.hour"; + public static final String BACKUPS_SCHEDULE_MINUTE = "settings.backups.schedule.minute"; public static final String SMS_DELIVERY_REPORTS_ENABLED = "settings.sms.delivery.reports.enabled"; public static final String WIFI_CALLING_COMPATIBILITY_MODE_ENABLED = "settings.wifi.calling.compatibility.mode.enabled"; public static final String MESSAGE_NOTIFICATIONS_ENABLED = "settings.message.notifications.enabled"; @@ -263,6 +265,19 @@ public final class SettingsValues extends SignalStoreValues { putBoolean(BACKUPS_ENABLED, backupEnabled); } + public int getBackupHour() { + return getInteger(BACKUPS_SCHEDULE_HOUR, 2); + } + + public int getBackupMinute() { + return getInteger(BACKUPS_SCHEDULE_MINUTE, 0); + } + + public void setBackupSchedule(int hour, int minute) { + putInteger(BACKUPS_SCHEDULE_HOUR, hour); + putInteger(BACKUPS_SCHEDULE_MINUTE, minute); + } + public boolean isSmsDeliveryReportsEnabled() { return getBoolean(SMS_DELIVERY_REPORTS_ENABLED, TextSecurePreferences.isSmsDeliveryReportsEnabled(ApplicationDependencies.getApplication())); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/BackupsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/BackupsPreferenceFragment.java index 03bc49575..55528e040 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/BackupsPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/BackupsPreferenceFragment.java @@ -2,11 +2,14 @@ package org.thoughtcrime.securesms.preferences; import android.Manifest; import android.app.Activity; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.text.format.DateFormat; import android.text.method.LinkMovementMethod; +import android.util.TimeUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,6 +22,9 @@ import androidx.annotation.RequiresApi; import androidx.core.text.HtmlCompat; import androidx.fragment.app.Fragment; +import com.google.android.material.timepicker.MaterialTimePicker; +import com.google.android.material.timepicker.TimeFormat; + import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -32,10 +38,14 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.LocalBackupJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.service.LocalBackupListener; import org.thoughtcrime.securesms.util.BackupUtil; +import org.thoughtcrime.securesms.util.JavaTimeExtensionsKt; import org.thoughtcrime.securesms.util.StorageUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.text.NumberFormat; +import java.time.LocalTime; import java.util.Locale; import java.util.Objects; @@ -48,6 +58,8 @@ public class BackupsPreferenceFragment extends Fragment { private View create; private View folder; private View verify; + private View timer; + private TextView timeLabel; private TextView toggle; private TextView info; private TextView summary; @@ -67,6 +79,8 @@ public class BackupsPreferenceFragment extends Fragment { create = view.findViewById(R.id.fragment_backup_create); folder = view.findViewById(R.id.fragment_backup_folder); verify = view.findViewById(R.id.fragment_backup_verify); + timer = view.findViewById(R.id.fragment_backup_time); + timeLabel = view.findViewById(R.id.fragment_backup_time_value); toggle = view.findViewById(R.id.fragment_backup_toggle); info = view.findViewById(R.id.fragment_backup_info); summary = view.findViewById(R.id.fragment_backup_create_summary); @@ -77,6 +91,7 @@ public class BackupsPreferenceFragment extends Fragment { toggle.setOnClickListener(unused -> onToggleClicked()); create.setOnClickListener(unused -> onCreateClicked()); verify.setOnClickListener(unused -> BackupDialog.showVerifyBackupPassphraseDialog(requireContext())); + timer.setOnClickListener(unused -> pickTime()); formatter.setMinimumFractionDigits(1); formatter.setMaximumFractionDigits(1); @@ -243,6 +258,25 @@ public class BackupsPreferenceFragment extends Fragment { LocalBackupJob.enqueue(true); } + private void pickTime() { + int timeFormat = DateFormat.is24HourFormat(requireContext()) ? TimeFormat.CLOCK_24H : TimeFormat.CLOCK_12H; + final MaterialTimePicker timePickerFragment = new MaterialTimePicker.Builder() + .setTimeFormat(timeFormat) + .setHour(SignalStore.settings().getBackupHour()) + .setMinute(SignalStore.settings().getBackupMinute()) + .setTitleText("Set Backup Time") + .build(); + timePickerFragment.addOnPositiveButtonClickListener(v -> { + int hour = timePickerFragment.getHour(); + int minute = timePickerFragment.getMinute(); + SignalStore.settings().setBackupSchedule(hour, minute); + updateTimeLabel(); + TextSecurePreferences.setNextBackupTime(requireContext(), 0); + LocalBackupListener.schedule(requireContext()); + }); + timePickerFragment.show(getChildFragmentManager(), "TIME_PICKER"); + } + private void onCreateClickedLegacy() { Permissions.with(this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -255,10 +289,19 @@ public class BackupsPreferenceFragment extends Fragment { .execute(); } + private void updateTimeLabel() { + final int backupHour = SignalStore.settings().getBackupHour(); + final int backupMinute = SignalStore.settings().getBackupMinute(); + LocalTime time = LocalTime.of(backupHour, backupMinute); + timeLabel.setText(JavaTimeExtensionsKt.formatHours(time, requireContext())); + } + private void setBackupsEnabled() { toggle.setText(R.string.BackupsPreferenceFragment__turn_off); create.setVisibility(View.VISIBLE); verify.setVisibility(View.VISIBLE); + timer.setVisibility(View.VISIBLE); + updateTimeLabel(); setBackupFolderName(); } @@ -267,6 +310,7 @@ public class BackupsPreferenceFragment extends Fragment { create.setVisibility(View.GONE); folder.setVisibility(View.GONE); verify.setVisibility(View.GONE); + timer.setVisibility(View.GONE); ApplicationDependencies.getJobManager().cancelAllInQueue(LocalBackupJob.QUEUE); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java index a8d68c254..23c23e466 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java @@ -50,7 +50,9 @@ public class LocalBackupListener extends PersistentAlarmManagerListener { nextTime = System.currentTimeMillis() + INTERVAL; } else { LocalDateTime now = LocalDateTime.now(); - LocalDateTime next = now.withHour(2).withMinute(0).withSecond(0); + int hour = SignalStore.settings().getBackupHour(); + int minute = SignalStore.settings().getBackupMinute(); + LocalDateTime next = now.withHour(hour).withMinute(minute).withSecond(0); if (now.getHour() >= 2) { next = next.plusDays(1); } diff --git a/app/src/main/res/layout/fragment_backups.xml b/app/src/main/res/layout/fragment_backups.xml index ef935a257..d3a076875 100644 --- a/app/src/main/res/layout/fragment_backups.xml +++ b/app/src/main/res/layout/fragment_backups.xml @@ -117,6 +117,37 @@ android:textColor="@color/signal_text_secondary" /> + + + + + + + + Create backup Last backup: %1$s Backup folder + + Backup time Verify backup passphrase Test your backup passphrase and verify that it matches Turn on