From 75d2d82d05c9419eb71d6dd8f61a630f5171caf4 Mon Sep 17 00:00:00 2001
From: Fynn Petersen-Frey <zoodyy@users.noreply.github.com>
Date: Wed, 31 Aug 2022 15:08:40 +0200
Subject: [PATCH] ask user to disable battery optimizations when turning on
 background backup (#554)

* ask user to disable battery optimizations when turning on background backup

* remove obsolete texts/translations

* add button link to dontkillmyapp
---
 .../example/mobile/BackgroundServicePlugin.kt | 22 +---------
 mobile/assets/i18n/en-US.json                 |  5 ++-
 mobile/assets/i18n/ko-KR.json                 |  1 -
 mobile/assets/i18n/nl-NL.json                 |  1 -
 mobile/assets/i18n/pt-BR.json                 |  1 -
 .../background.service.dart                   | 12 ++---
 .../backup/providers/backup.provider.dart     |  5 ++-
 .../backup/views/backup_controller_page.dart  | 44 +++++++++++++++++++
 8 files changed, 58 insertions(+), 33 deletions(-)

diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt
index c3fb7a209e..04aa6f1b3d 100644
--- a/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt
+++ b/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt
@@ -69,26 +69,8 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
             "isEnabled" -> {
                 result.success(BackupWorker.isEnabled(ctx))
             }
-            "disableBatteryOptimizations" -> {
-                if(!BackupWorker.isIgnoringBatteryOptimizations(ctx)) {
-                    val args = call.arguments<ArrayList<*>>()!!
-                    val text = args.get(0) as String
-                    Toast.makeText(ctx, text, Toast.LENGTH_LONG).show()
-                    val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                    intent.setData(Uri.parse("package:" + ctx.getPackageName()))
-                    try {
-                        ctx.startActivity(intent)
-                    } catch(e: Exception) {
-                        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
-                        try {
-                            ctx.startActivity(intent)
-                        } catch (e2: Exception) {
-                            return result.success(false)
-                        }
-                    }
-                }
-                result.success(true)
+            "isIgnoringBatteryOptimizations" -> {
+                result.success(BackupWorker.isIgnoringBatteryOptimizations(ctx))
             }
             else -> result.notImplemented()
         }
diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json
index f9e7e843d4..1412b1fe78 100644
--- a/mobile/assets/i18n/en-US.json
+++ b/mobile/assets/i18n/en-US.json
@@ -17,7 +17,6 @@
   "backup_album_selection_page_total_assets": "Total unique assets",
   "backup_all": "All",
   "backup_background_service_default_notification": "Checking for new assets…",
-  "backup_background_service_disable_battery_optimizations": "Please disable battery optimization for Immich to enable background backup",
   "backup_background_service_upload_failure_notification": "Failed to upload {}",
   "backup_background_service_in_progress_notification": "Backing up your assets…",
   "backup_background_service_current_upload_notification": "Uploading {}",
@@ -36,6 +35,10 @@
   "backup_controller_page_background_turn_on": "Turn on background service",
   "backup_controller_page_background_turn_off": "Turn off background service",
   "backup_controller_page_background_configure_error": "Failed to configure the background service",
+  "backup_controller_page_background_battery_info_title": "Battery optimizations",
+  "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
+  "backup_controller_page_background_battery_info_link": "Show me how",
+  "backup_controller_page_background_battery_info_ok": "OK",
   "backup_controller_page_cancel": "Cancel",
   "backup_controller_page_created": "Created on: {}",
   "backup_controller_page_desc_backup": "Turn on backup to automatically upload new assets to the server.",
diff --git a/mobile/assets/i18n/ko-KR.json b/mobile/assets/i18n/ko-KR.json
index dd4acf1568..22e843fcc4 100644
--- a/mobile/assets/i18n/ko-KR.json
+++ b/mobile/assets/i18n/ko-KR.json
@@ -17,7 +17,6 @@
   "backup_album_selection_page_total_assets": "총 미디어파일 수",
   "backup_all": "모두",
   "backup_background_service_default_notification": "새 미디어파일 확인중...",
-  "backup_background_service_disable_battery_optimizations": "백그라운드 백업을 활성화하려면 Immich에 대한 배터리최적화를 비활성화 해야합니다.",
   "backup_background_service_upload_failure_notification": "{} 업로드 실패",
   "backup_background_service_in_progress_notification": "미디어파일 백업 중...",
   "backup_background_service_current_upload_notification": "{} 업로드 중",
diff --git a/mobile/assets/i18n/nl-NL.json b/mobile/assets/i18n/nl-NL.json
index 0f3a0bc200..d27232b533 100644
--- a/mobile/assets/i18n/nl-NL.json
+++ b/mobile/assets/i18n/nl-NL.json
@@ -17,7 +17,6 @@
   "backup_album_selection_page_total_assets": "Totaal unieke items",
   "backup_all": "Alle",
   "backup_background_service_default_notification": "Controleren op nieuw items…",
-  "backup_background_service_disable_battery_optimizations": "Schakel batterij optimalisatie uit voor Immich om achtergrond backup in te schakelen",
   "backup_background_service_upload_failure_notification": "Fout bij upload {}",
   "backup_background_service_in_progress_notification": "Backuppen van items…",
   "backup_background_service_current_upload_notification": "Uploaden {}",
diff --git a/mobile/assets/i18n/pt-BR.json b/mobile/assets/i18n/pt-BR.json
index b0e96ad31d..6b3ec1dd65 100644
--- a/mobile/assets/i18n/pt-BR.json
+++ b/mobile/assets/i18n/pt-BR.json
@@ -17,7 +17,6 @@
     "backup_album_selection_page_total_assets": "Total de recursos exclusivos",
     "backup_all": "Todos",
     "backup_background_service_default_notification": "Checking for new assets…",
-    "backup_background_service_disable_battery_optimizations": "Por favor, desabilite a otimização da bateria para Immich para habilitar o backup em segundo plano",
     "backup_background_service_upload_failure_notification": "Falha ao carregar {}",
     "backup_background_service_in_progress_notification": "Fazendo backup de seus ativos…",
     "backup_background_service_current_upload_notification": "Enviando {}",
diff --git a/mobile/lib/modules/backup/background_service/background.service.dart b/mobile/lib/modules/backup/background_service/background.service.dart
index 5581831513..44c47aacd0 100644
--- a/mobile/lib/modules/backup/background_service/background.service.dart
+++ b/mobile/lib/modules/backup/background_service/background.service.dart
@@ -122,8 +122,8 @@ class BackgroundService {
     }
   }
 
-  /// Opens an activity to let the user disable battery optimizations for Immich
-  Future<bool> disableBatteryOptimizations() async {
+  /// Returns `true` if battery optimizations are disabled
+  Future<bool> isIgnoringBatteryOptimizations() async {
     if (!Platform.isAndroid) {
       return true;
     }
@@ -131,12 +131,8 @@ class BackgroundService {
       if (!_isForegroundInitialized) {
         await _initialize();
       }
-      final String message =
-          "backup_background_service_disable_battery_optimizations".tr();
-      return await _foregroundChannel.invokeMethod(
-        'disableBatteryOptimizations',
-        message,
-      );
+      return await _foregroundChannel
+          .invokeMethod('isIgnoringBatteryOptimizations');
     } catch (error) {
       return false;
     }
diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart
index 37082bfe98..545448197e 100644
--- a/mobile/lib/modules/backup/providers/backup.provider.dart
+++ b/mobile/lib/modules/backup/providers/backup.provider.dart
@@ -117,6 +117,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
     bool? requireWifi,
     bool? requireCharging,
     required void Function(String msg) onError,
+    required void Function() onBatteryInfo,
   }) async {
     assert(enabled != null || requireWifi != null || requireCharging != null);
     if (Platform.isAndroid) {
@@ -131,7 +132,9 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 
       if (state.backgroundBackup) {
         if (!wasEnabled) {
-          await _backgroundService.disableBatteryOptimizations();
+          if (!await _backgroundService.isIgnoringBatteryOptimizations()) {
+            onBatteryInfo();
+          }
         }
         final bool success = await _backgroundService.stopService() &&
             await _backgroundService.startService(
diff --git a/mobile/lib/modules/backup/views/backup_controller_page.dart b/mobile/lib/modules/backup/views/backup_controller_page.dart
index 75ff14c67d..715b449f7c 100644
--- a/mobile/lib/modules/backup/views/backup_controller_page.dart
+++ b/mobile/lib/modules/backup/views/backup_controller_page.dart
@@ -14,6 +14,7 @@ import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/providers/websocket.provider.dart';
 import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
 import 'package:percent_indicator/linear_percent_indicator.dart';
+import 'package:url_launcher/url_launcher.dart';
 
 class BackupControllerPage extends HookConsumerWidget {
   const BackupControllerPage({Key? key}) : super(key: key);
@@ -156,6 +157,46 @@ class BackupControllerPage extends HookConsumerWidget {
       ScaffoldMessenger.of(context).showSnackBar(snackBar);
     }
 
+    void _showBatteryOptimizationInfoToUser() {
+      final buttonTextColor = Theme.of(context).primaryColor;
+      showDialog<void>(
+        context: context,
+        barrierDismissible: false,
+        builder: (BuildContext context) {
+          return AlertDialog(
+            title: const Text(
+              'backup_controller_page_background_battery_info_title',
+            ).tr(),
+            content: SingleChildScrollView(
+              child: const Text(
+                'backup_controller_page_background_battery_info_message',
+              ).tr(),
+            ),
+            actions: [
+              TextButton(
+                onPressed: () => launchUrl(
+                    Uri.parse('https://dontkillmyapp.com'),
+                    mode: LaunchMode.externalApplication),
+                child: Text(
+                  "backup_controller_page_background_battery_info_link",
+                  style: TextStyle(color: buttonTextColor),
+                ).tr(),
+              ),
+              TextButton(
+                child: Text(
+                  'backup_controller_page_background_battery_info_ok',
+                  style: TextStyle(color: buttonTextColor),
+                ).tr(),
+                onPressed: () {
+                  Navigator.of(context).pop();
+                },
+              ),
+            ],
+          );
+        },
+      );
+    }
+
     ListTile _buildBackgroundBackupController() {
       final bool isBackgroundEnabled = backupState.backgroundBackup;
       final bool isWifiRequired = backupState.backupRequireWifi;
@@ -197,6 +238,7 @@ class BackupControllerPage extends HookConsumerWidget {
                         .configureBackgroundBackup(
                           requireWifi: isChecked,
                           onError: _showErrorToUser,
+                          onBatteryInfo: _showBatteryOptimizationInfoToUser,
                         )
                     : null,
               ),
@@ -217,6 +259,7 @@ class BackupControllerPage extends HookConsumerWidget {
                         .configureBackgroundBackup(
                           requireCharging: isChecked,
                           onError: _showErrorToUser,
+                          onBatteryInfo: _showBatteryOptimizationInfoToUser,
                         )
                     : null,
               ),
@@ -225,6 +268,7 @@ class BackupControllerPage extends HookConsumerWidget {
                   ref.read(backupProvider.notifier).configureBackgroundBackup(
                         enabled: !isBackgroundEnabled,
                         onError: _showErrorToUser,
+                        onBatteryInfo: _showBatteryOptimizationInfoToUser,
                       ),
               child: Text(
                 isBackgroundEnabled