mirror of
https://github.com/immich-app/immich.git
synced 2025-05-13 22:26:49 +02:00
feat(mobile): configurable background backup delay (#1068)
let's the user configure how much to delay the trigger for running the backup whenever assets are changed on the device
This commit is contained in:
parent
a97b761eda
commit
c23b2479f7
@ -54,7 +54,9 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
|
|||||||
val args = call.arguments<ArrayList<*>>()!!
|
val args = call.arguments<ArrayList<*>>()!!
|
||||||
val requireUnmeteredNetwork = args.get(0) as Boolean
|
val requireUnmeteredNetwork = args.get(0) as Boolean
|
||||||
val requireCharging = args.get(1) as Boolean
|
val requireCharging = args.get(1) as Boolean
|
||||||
ContentObserverWorker.configureWork(ctx, requireUnmeteredNetwork, requireCharging)
|
val triggerUpdateDelay = (args.get(2) as Number).toLong()
|
||||||
|
val triggerMaxDelay = (args.get(3) as Number).toLong()
|
||||||
|
ContentObserverWorker.configureWork(ctx, requireUnmeteredNetwork, requireCharging, triggerUpdateDelay, triggerMaxDelay)
|
||||||
result.success(true)
|
result.success(true)
|
||||||
}
|
}
|
||||||
"disable" -> {
|
"disable" -> {
|
||||||
|
@ -37,6 +37,8 @@ class ContentObserverWorker(ctx: Context, params: WorkerParameters) : Worker(ctx
|
|||||||
const val SHARED_PREF_SERVICE_ENABLED = "serviceEnabled"
|
const val SHARED_PREF_SERVICE_ENABLED = "serviceEnabled"
|
||||||
const val SHARED_PREF_REQUIRE_WIFI = "requireWifi"
|
const val SHARED_PREF_REQUIRE_WIFI = "requireWifi"
|
||||||
const val SHARED_PREF_REQUIRE_CHARGING = "requireCharging"
|
const val SHARED_PREF_REQUIRE_CHARGING = "requireCharging"
|
||||||
|
const val SHARED_PREF_TRIGGER_UPDATE_DELAY = "triggerUpdateDelay"
|
||||||
|
const val SHARED_PREF_TRIGGER_MAX_DELAY = "triggerMaxDelay"
|
||||||
|
|
||||||
private const val TASK_NAME_OBSERVER = "immich/ContentObserver"
|
private const val TASK_NAME_OBSERVER = "immich/ContentObserver"
|
||||||
|
|
||||||
@ -62,12 +64,16 @@ class ContentObserverWorker(ctx: Context, params: WorkerParameters) : Worker(ctx
|
|||||||
*/
|
*/
|
||||||
fun configureWork(context: Context,
|
fun configureWork(context: Context,
|
||||||
requireWifi: Boolean = false,
|
requireWifi: Boolean = false,
|
||||||
requireCharging: Boolean = false) {
|
requireCharging: Boolean = false,
|
||||||
|
triggerUpdateDelay: Long = 5000,
|
||||||
|
triggerMaxDelay: Long = 50000) {
|
||||||
context.getSharedPreferences(BackupWorker.SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
context.getSharedPreferences(BackupWorker.SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean(SHARED_PREF_SERVICE_ENABLED, true)
|
.putBoolean(SHARED_PREF_SERVICE_ENABLED, true)
|
||||||
.putBoolean(SHARED_PREF_REQUIRE_WIFI, requireWifi)
|
.putBoolean(SHARED_PREF_REQUIRE_WIFI, requireWifi)
|
||||||
.putBoolean(SHARED_PREF_REQUIRE_CHARGING, requireCharging)
|
.putBoolean(SHARED_PREF_REQUIRE_CHARGING, requireCharging)
|
||||||
|
.putLong(SHARED_PREF_TRIGGER_UPDATE_DELAY, triggerUpdateDelay)
|
||||||
|
.putLong(SHARED_PREF_TRIGGER_MAX_DELAY, triggerMaxDelay)
|
||||||
.apply()
|
.apply()
|
||||||
BackupWorker.updateBackupWorker(context, requireWifi, requireCharging)
|
BackupWorker.updateBackupWorker(context, requireWifi, requireCharging)
|
||||||
}
|
}
|
||||||
@ -106,12 +112,14 @@ class ContentObserverWorker(ctx: Context, params: WorkerParameters) : Worker(ctx
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun enqueueObserverWorker(context: Context, policy: ExistingWorkPolicy) {
|
private fun enqueueObserverWorker(context: Context, policy: ExistingWorkPolicy) {
|
||||||
|
val sp = context.getSharedPreferences(BackupWorker.SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||||
val constraints = Constraints.Builder()
|
val constraints = Constraints.Builder()
|
||||||
.addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true)
|
.addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true)
|
||||||
.addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true)
|
.addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true)
|
||||||
.addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true)
|
.addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true)
|
||||||
.addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true)
|
.addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true)
|
||||||
.setTriggerContentUpdateDelay(5000, TimeUnit.MILLISECONDS)
|
.setTriggerContentUpdateDelay(sp.getLong(SHARED_PREF_TRIGGER_UPDATE_DELAY, 5000), TimeUnit.MILLISECONDS)
|
||||||
|
.setTriggerContentMaxDelay(sp.getLong(SHARED_PREF_TRIGGER_MAX_DELAY, 50000), TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val work = OneTimeWorkRequest.Builder(ContentObserverWorker::class.java)
|
val work = OneTimeWorkRequest.Builder(ContentObserverWorker::class.java)
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"backup_controller_page_background_turn_off": "Turn off background service",
|
"backup_controller_page_background_turn_off": "Turn off background service",
|
||||||
"backup_controller_page_background_turn_on": "Turn on background service",
|
"backup_controller_page_background_turn_on": "Turn on background service",
|
||||||
"backup_controller_page_background_wifi": "Only on WiFi",
|
"backup_controller_page_background_wifi": "Only on WiFi",
|
||||||
|
"backup_controller_page_background_delay": "Delay new assets backup: {}",
|
||||||
"backup_controller_page_backup": "Backup",
|
"backup_controller_page_backup": "Backup",
|
||||||
"backup_controller_page_backup_selected": "Selected: ",
|
"backup_controller_page_backup_selected": "Selected: ",
|
||||||
"backup_controller_page_backup_sub": "Backed up photos and videos",
|
"backup_controller_page_backup_sub": "Backed up photos and videos",
|
||||||
@ -134,6 +135,7 @@
|
|||||||
"setting_notifications_notify_hours": "{} hours",
|
"setting_notifications_notify_hours": "{} hours",
|
||||||
"setting_notifications_notify_immediately": "immediately",
|
"setting_notifications_notify_immediately": "immediately",
|
||||||
"setting_notifications_notify_minutes": "{} minutes",
|
"setting_notifications_notify_minutes": "{} minutes",
|
||||||
|
"setting_notifications_notify_seconds": "{} seconds",
|
||||||
"setting_notifications_notify_never": "never",
|
"setting_notifications_notify_never": "never",
|
||||||
"setting_notifications_subtitle": "Adjust your notification preferences",
|
"setting_notifications_subtitle": "Adjust your notification preferences",
|
||||||
"setting_notifications_title": "Notifications",
|
"setting_notifications_title": "Notifications",
|
||||||
|
@ -26,6 +26,7 @@ const String backgroundBackupInfoBox = "immichBackgroundBackupInfoBox"; // Box
|
|||||||
const String backupFailedSince = "immichBackupFailedSince"; // Key 1
|
const String backupFailedSince = "immichBackupFailedSince"; // Key 1
|
||||||
const String backupRequireWifi = "immichBackupRequireWifi"; // Key 2
|
const String backupRequireWifi = "immichBackupRequireWifi"; // Key 2
|
||||||
const String backupRequireCharging = "immichBackupRequireCharging"; // Key 3
|
const String backupRequireCharging = "immichBackupRequireCharging"; // Key 3
|
||||||
|
const String backupTriggerDelay = "immichBackupTriggerDelay"; // Key 4
|
||||||
|
|
||||||
// Duplicate asset
|
// Duplicate asset
|
||||||
const String duplicatedAssetsBox = "immichDuplicatedAssetsBox"; // Box
|
const String duplicatedAssetsBox = "immichDuplicatedAssetsBox"; // Box
|
||||||
|
@ -86,6 +86,8 @@ class BackgroundService {
|
|||||||
Future<bool> configureService({
|
Future<bool> configureService({
|
||||||
bool requireUnmetered = true,
|
bool requireUnmetered = true,
|
||||||
bool requireCharging = false,
|
bool requireCharging = false,
|
||||||
|
int triggerUpdateDelay = 5000,
|
||||||
|
int triggerMaxDelay = 50000,
|
||||||
}) async {
|
}) async {
|
||||||
if (!Platform.isAndroid) {
|
if (!Platform.isAndroid) {
|
||||||
return true;
|
return true;
|
||||||
@ -93,7 +95,12 @@ class BackgroundService {
|
|||||||
try {
|
try {
|
||||||
final bool ok = await _foregroundChannel.invokeMethod(
|
final bool ok = await _foregroundChannel.invokeMethod(
|
||||||
'configure',
|
'configure',
|
||||||
[requireUnmetered, requireCharging],
|
[
|
||||||
|
requireUnmetered,
|
||||||
|
requireCharging,
|
||||||
|
triggerUpdateDelay,
|
||||||
|
triggerMaxDelay
|
||||||
|
],
|
||||||
);
|
);
|
||||||
return ok;
|
return ok;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -18,6 +18,7 @@ class BackUpState {
|
|||||||
final bool backgroundBackup;
|
final bool backgroundBackup;
|
||||||
final bool backupRequireWifi;
|
final bool backupRequireWifi;
|
||||||
final bool backupRequireCharging;
|
final bool backupRequireCharging;
|
||||||
|
final int backupTriggerDelay;
|
||||||
|
|
||||||
/// All available albums on the device
|
/// All available albums on the device
|
||||||
final List<AvailableAlbum> availableAlbums;
|
final List<AvailableAlbum> availableAlbums;
|
||||||
@ -42,6 +43,7 @@ class BackUpState {
|
|||||||
required this.backgroundBackup,
|
required this.backgroundBackup,
|
||||||
required this.backupRequireWifi,
|
required this.backupRequireWifi,
|
||||||
required this.backupRequireCharging,
|
required this.backupRequireCharging,
|
||||||
|
required this.backupTriggerDelay,
|
||||||
required this.availableAlbums,
|
required this.availableAlbums,
|
||||||
required this.selectedBackupAlbums,
|
required this.selectedBackupAlbums,
|
||||||
required this.excludedBackupAlbums,
|
required this.excludedBackupAlbums,
|
||||||
@ -59,6 +61,7 @@ class BackUpState {
|
|||||||
bool? backgroundBackup,
|
bool? backgroundBackup,
|
||||||
bool? backupRequireWifi,
|
bool? backupRequireWifi,
|
||||||
bool? backupRequireCharging,
|
bool? backupRequireCharging,
|
||||||
|
int? backupTriggerDelay,
|
||||||
List<AvailableAlbum>? availableAlbums,
|
List<AvailableAlbum>? availableAlbums,
|
||||||
Set<AvailableAlbum>? selectedBackupAlbums,
|
Set<AvailableAlbum>? selectedBackupAlbums,
|
||||||
Set<AvailableAlbum>? excludedBackupAlbums,
|
Set<AvailableAlbum>? excludedBackupAlbums,
|
||||||
@ -76,6 +79,7 @@ class BackUpState {
|
|||||||
backupRequireWifi: backupRequireWifi ?? this.backupRequireWifi,
|
backupRequireWifi: backupRequireWifi ?? this.backupRequireWifi,
|
||||||
backupRequireCharging:
|
backupRequireCharging:
|
||||||
backupRequireCharging ?? this.backupRequireCharging,
|
backupRequireCharging ?? this.backupRequireCharging,
|
||||||
|
backupTriggerDelay: backupTriggerDelay ?? this.backupTriggerDelay,
|
||||||
availableAlbums: availableAlbums ?? this.availableAlbums,
|
availableAlbums: availableAlbums ?? this.availableAlbums,
|
||||||
selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
|
selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
|
||||||
excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums,
|
excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums,
|
||||||
@ -88,7 +92,7 @@ class BackUpState {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, cancelToken: $cancelToken, serverInfo: $serverInfo, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)';
|
return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, cancelToken: $cancelToken, serverInfo: $serverInfo, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, backupTriggerDelay: $backupTriggerDelay, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -105,6 +109,7 @@ class BackUpState {
|
|||||||
other.backgroundBackup == backgroundBackup &&
|
other.backgroundBackup == backgroundBackup &&
|
||||||
other.backupRequireWifi == backupRequireWifi &&
|
other.backupRequireWifi == backupRequireWifi &&
|
||||||
other.backupRequireCharging == backupRequireCharging &&
|
other.backupRequireCharging == backupRequireCharging &&
|
||||||
|
other.backupTriggerDelay == backupTriggerDelay &&
|
||||||
collectionEquals(other.availableAlbums, availableAlbums) &&
|
collectionEquals(other.availableAlbums, availableAlbums) &&
|
||||||
collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
|
collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
|
||||||
collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
|
collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
|
||||||
@ -126,6 +131,7 @@ class BackUpState {
|
|||||||
backgroundBackup.hashCode ^
|
backgroundBackup.hashCode ^
|
||||||
backupRequireWifi.hashCode ^
|
backupRequireWifi.hashCode ^
|
||||||
backupRequireCharging.hashCode ^
|
backupRequireCharging.hashCode ^
|
||||||
|
backupTriggerDelay.hashCode ^
|
||||||
availableAlbums.hashCode ^
|
availableAlbums.hashCode ^
|
||||||
selectedBackupAlbums.hashCode ^
|
selectedBackupAlbums.hashCode ^
|
||||||
excludedBackupAlbums.hashCode ^
|
excludedBackupAlbums.hashCode ^
|
||||||
|
@ -38,6 +38,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
backgroundBackup: false,
|
backgroundBackup: false,
|
||||||
backupRequireWifi: true,
|
backupRequireWifi: true,
|
||||||
backupRequireCharging: false,
|
backupRequireCharging: false,
|
||||||
|
backupTriggerDelay: 5000,
|
||||||
serverInfo: ServerInfoResponseDto(
|
serverInfo: ServerInfoResponseDto(
|
||||||
diskAvailable: "0",
|
diskAvailable: "0",
|
||||||
diskAvailableRaw: 0,
|
diskAvailableRaw: 0,
|
||||||
@ -119,18 +120,26 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
bool? enabled,
|
bool? enabled,
|
||||||
bool? requireWifi,
|
bool? requireWifi,
|
||||||
bool? requireCharging,
|
bool? requireCharging,
|
||||||
|
int? triggerDelay,
|
||||||
required void Function(String msg) onError,
|
required void Function(String msg) onError,
|
||||||
required void Function() onBatteryInfo,
|
required void Function() onBatteryInfo,
|
||||||
}) async {
|
}) async {
|
||||||
assert(enabled != null || requireWifi != null || requireCharging != null);
|
assert(
|
||||||
|
enabled != null ||
|
||||||
|
requireWifi != null ||
|
||||||
|
requireCharging != null ||
|
||||||
|
triggerDelay != null,
|
||||||
|
);
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
final bool wasEnabled = state.backgroundBackup;
|
final bool wasEnabled = state.backgroundBackup;
|
||||||
final bool wasWifi = state.backupRequireWifi;
|
final bool wasWifi = state.backupRequireWifi;
|
||||||
final bool wasCharing = state.backupRequireCharging;
|
final bool wasCharging = state.backupRequireCharging;
|
||||||
|
final int oldTriggerDelay = state.backupTriggerDelay;
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backgroundBackup: enabled,
|
backgroundBackup: enabled,
|
||||||
backupRequireWifi: requireWifi,
|
backupRequireWifi: requireWifi,
|
||||||
backupRequireCharging: requireCharging,
|
backupRequireCharging: requireCharging,
|
||||||
|
backupTriggerDelay: triggerDelay,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (state.backgroundBackup) {
|
if (state.backgroundBackup) {
|
||||||
@ -145,17 +154,22 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
await _backgroundService.configureService(
|
await _backgroundService.configureService(
|
||||||
requireUnmetered: state.backupRequireWifi,
|
requireUnmetered: state.backupRequireWifi,
|
||||||
requireCharging: state.backupRequireCharging,
|
requireCharging: state.backupRequireCharging,
|
||||||
|
triggerUpdateDelay: state.backupTriggerDelay,
|
||||||
|
triggerMaxDelay: state.backupTriggerDelay * 10,
|
||||||
);
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
await Hive.box(backgroundBackupInfoBox)
|
final box = Hive.box(backgroundBackupInfoBox);
|
||||||
.put(backupRequireWifi, state.backupRequireWifi);
|
await Future.wait([
|
||||||
await Hive.box(backgroundBackupInfoBox)
|
box.put(backupRequireWifi, state.backupRequireWifi),
|
||||||
.put(backupRequireCharging, state.backupRequireCharging);
|
box.put(backupRequireCharging, state.backupRequireCharging),
|
||||||
|
box.put(backupTriggerDelay, state.backupTriggerDelay),
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
backgroundBackup: wasEnabled,
|
backgroundBackup: wasEnabled,
|
||||||
backupRequireWifi: wasWifi,
|
backupRequireWifi: wasWifi,
|
||||||
backupRequireCharging: wasCharing,
|
backupRequireCharging: wasCharging,
|
||||||
|
backupTriggerDelay: oldTriggerDelay,
|
||||||
);
|
);
|
||||||
onError("backup_controller_page_background_configure_error");
|
onError("backup_controller_page_background_configure_error");
|
||||||
}
|
}
|
||||||
@ -602,6 +616,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
excludedBackupAlbums: excludedAlbums,
|
excludedBackupAlbums: excludedAlbums,
|
||||||
backupRequireWifi: backgroundBox.get(backupRequireWifi),
|
backupRequireWifi: backgroundBox.get(backupRequireWifi),
|
||||||
backupRequireCharging: backgroundBox.get(backupRequireCharging),
|
backupRequireCharging: backgroundBox.get(backupRequireCharging),
|
||||||
|
backupTriggerDelay: backgroundBox.get(backupTriggerDelay),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _resumeBackup();
|
return _resumeBackup();
|
||||||
|
@ -198,6 +198,46 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
final bool isWifiRequired = backupState.backupRequireWifi;
|
final bool isWifiRequired = backupState.backupRequireWifi;
|
||||||
final bool isChargingRequired = backupState.backupRequireCharging;
|
final bool isChargingRequired = backupState.backupRequireCharging;
|
||||||
final Color activeColor = Theme.of(context).primaryColor;
|
final Color activeColor = Theme.of(context).primaryColor;
|
||||||
|
|
||||||
|
String formatBackupDelaySliderValue(double v) {
|
||||||
|
if (v == 0.0) {
|
||||||
|
return 'setting_notifications_notify_seconds'.tr(args: const ['5']);
|
||||||
|
} else if (v == 1.0) {
|
||||||
|
return 'setting_notifications_notify_seconds'.tr(args: const ['30']);
|
||||||
|
} else if (v == 2.0) {
|
||||||
|
return 'setting_notifications_notify_minutes'.tr(args: const ['2']);
|
||||||
|
} else {
|
||||||
|
return 'setting_notifications_notify_minutes'.tr(args: const ['10']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int backupDelayToMilliseconds(double v) {
|
||||||
|
if (v == 0.0) {
|
||||||
|
return 5000;
|
||||||
|
} else if (v == 1.0) {
|
||||||
|
return 30000;
|
||||||
|
} else if (v == 2.0) {
|
||||||
|
return 120000;
|
||||||
|
} else {
|
||||||
|
return 600000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double backupDelayToSliderValue(int ms) {
|
||||||
|
if (ms == 5000) {
|
||||||
|
return 0.0;
|
||||||
|
} else if (ms == 30000) {
|
||||||
|
return 1.0;
|
||||||
|
} else if (ms == 120000) {
|
||||||
|
return 2.0;
|
||||||
|
} else {
|
||||||
|
return 3.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final triggerDelay =
|
||||||
|
useState(backupDelayToSliderValue(backupState.backupTriggerDelay));
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
leading: isBackgroundEnabled
|
leading: isBackgroundEnabled
|
||||||
@ -264,6 +304,35 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
if (isBackgroundEnabled)
|
||||||
|
ListTile(
|
||||||
|
isThreeLine: false,
|
||||||
|
dense: true,
|
||||||
|
enabled: hasExclusiveAccess,
|
||||||
|
title: const Text(
|
||||||
|
'backup_controller_page_background_delay',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
).tr(args: [formatBackupDelaySliderValue(triggerDelay.value)]),
|
||||||
|
subtitle: Slider(
|
||||||
|
value: triggerDelay.value,
|
||||||
|
onChanged: hasExclusiveAccess
|
||||||
|
? (double v) => triggerDelay.value = v
|
||||||
|
: null,
|
||||||
|
onChangeEnd: (double v) => ref
|
||||||
|
.read(backupProvider.notifier)
|
||||||
|
.configureBackgroundBackup(
|
||||||
|
triggerDelay: backupDelayToMilliseconds(v),
|
||||||
|
onError: showErrorToUser,
|
||||||
|
onBatteryInfo: showBatteryOptimizationInfoToUser,
|
||||||
|
),
|
||||||
|
max: 3.0,
|
||||||
|
divisions: 3,
|
||||||
|
label: formatBackupDelaySliderValue(triggerDelay.value),
|
||||||
|
activeColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
ref.read(backupProvider.notifier).configureBackgroundBackup(
|
ref.read(backupProvider.notifier).configureBackgroundBackup(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user