diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml index 6f646b913..b3c655917 100644 --- a/flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter/android/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ + { + val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE) + result.success(prefs.getBoolean(KEY_START_ON_BOOT_OPT, false)) + } + SET_START_ON_BOOT_OPT -> { + try { + if (call.arguments is Boolean) { + val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE) + val edit = prefs.edit() + edit.putBoolean(KEY_START_ON_BOOT_OPT, call.arguments as Boolean) + edit.apply() + result.success(true) + } else { + result.success(false) + } + } finally { + result.success(false) + } + } else -> { result.error("-1", "No such method", null) } diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt index 0bc6c1c28..2d53ea010 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt @@ -37,9 +37,14 @@ const val REQ_REQUEST_MEDIA_PROJECTION = 201 const val RES_FAILED = -100 // Flutter channel -const val START_ACTION = "start_action"; -const val IGNORE_BATTERY_OPTIMIZATIONS = "ignore_battery_optimizations"; +const val START_ACTION = "start_action" +const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt" +const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt" +const val IGNORE_BATTERY_OPTIMIZATIONS = "ignore_battery_optimizations" + +const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES" +const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT" @SuppressLint("ConstantLocale") val LOCAL_NAME = Locale.getDefault().toString() diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index a0766a874..1dc1c0b5a 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -150,7 +150,11 @@ const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW"; const kIgnoreBatteryOptimizations = "ignore_battery_optimizations"; /// Android channel invoke type key -const kStartAction = "start_action"; +class AndroidChannel { + static final kStartAction = "start_action"; + static final kGetStartOnBootOpt = "get_start_on_boot_opt"; + static final kSetStartOnBootOpt = "set_start_on_boot_opt"; +} /// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels /// see [LogicalKeyboardKey.keyLabel] diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 6bdb7e813..0e160396b 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -32,18 +32,21 @@ class SettingsPage extends StatefulWidget implements PageShape { } const url = 'https://rustdesk.com/'; -final _hasIgnoreBattery = androidVersion >= 26; -var _ignoreBatteryOpt = false; -var _enableAbr = false; -var _denyLANDiscovery = false; -var _onlyWhiteList = false; -var _enableDirectIPAccess = false; -var _enableRecordSession = false; -var _autoRecordIncomingSession = false; -var _localIP = ""; -var _directAccessPort = ""; class _SettingsState extends State with WidgetsBindingObserver { + final _hasIgnoreBattery = androidVersion >= 26; + var _ignoreBatteryOpt = false; + var _systemAlertWindow = false; + var _enableStartOnBoot = false; + var _enableAbr = false; + var _denyLANDiscovery = false; + var _onlyWhiteList = false; + var _enableDirectIPAccess = false; + var _enableRecordSession = false; + var _autoRecordIncomingSession = false; + var _localIP = ""; + var _directAccessPort = ""; + @override void initState() { super.initState(); @@ -51,11 +54,35 @@ class _SettingsState extends State with WidgetsBindingObserver { () async { var update = false; + if (_hasIgnoreBattery) { - update = await updateIgnoreBatteryStatus(); + if (await checkAndUpdateIgnoreBatteryStatus()) { + update = true; + } } - final enableAbrRes = await bind.mainGetOption(key: "enable-abr") != "N"; + if (await checkAndUpdateSystemAlertWindow()) { + update = true; + } + + // TODO need input + // start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW + var enableStartOnBoot = + await gFFI.invokeMethod(AndroidChannel.kGetStartOnBootOpt); + if (enableStartOnBoot) { + if (!canStartOnBoot()) { + enableStartOnBoot = false; + gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false); + } + } + + if (enableStartOnBoot != _enableStartOnBoot) { + update = true; + _enableStartOnBoot = enableStartOnBoot; + } + + final enableAbrRes = option2bool( + "enable-abr", await bind.mainGetOption(key: "enable-abr")); if (enableAbrRes != _enableAbr) { update = true; _enableAbr = enableAbrRes; @@ -126,14 +153,15 @@ class _SettingsState extends State with WidgetsBindingObserver { void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { () async { - if (await updateIgnoreBatteryStatus()) { + if (await checkAndUpdateIgnoreBatteryStatus() || + await checkAndUpdateSystemAlertWindow()) { setState(() {}); } }(); } } - Future updateIgnoreBatteryStatus() async { + Future checkAndUpdateIgnoreBatteryStatus() async { final res = await AndroidPermissionManager.check(kIgnoreBatteryOptimizations); if (_ignoreBatteryOpt != res) { @@ -144,6 +172,16 @@ class _SettingsState extends State with WidgetsBindingObserver { } } + Future checkAndUpdateSystemAlertWindow() async { + final res = await AndroidPermissionManager.check(kSystemAlertWindow); + if (_systemAlertWindow != res) { + _systemAlertWindow = res; + return true; + } else { + return false; + } + } + @override Widget build(BuildContext context) { Provider.of(context); @@ -267,8 +305,8 @@ class _SettingsState extends State with WidgetsBindingObserver { ]), onToggle: (v) async { if (v) { - gFFI.invokeMethod( - kStartAction, kActionRequestIgnoreBatteryOptimizations); + gFFI.invokeMethod(AndroidChannel.kStartAction, + kActionRequestIgnoreBatteryOptimizations); } else { final res = await gFFI.dialogManager .show((setState, close) => CustomAlertDialog( @@ -285,12 +323,27 @@ class _SettingsState extends State with WidgetsBindingObserver { ], )); if (res == true) { - gFFI.invokeMethod( - kStartAction, kActionApplicationDetailsSettings); + gFFI.invokeMethod(AndroidChannel.kStartAction, + kActionApplicationDetailsSettings); } } })); } + enhancementsTiles.add(SettingsTile.switchTile( + initialValue: _enableStartOnBoot, + title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text("$translate('Start on Boot') (beta)"), + Text( + '* ${translate('Start the screen recording service on boot, which requires special permissions')}', + style: Theme.of(context).textTheme.bodySmall), + ]), + onToggle: (v) async { + if (v) { + // TODO + } else { + gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false); + } + })); return SettingsList( sections: [ @@ -391,6 +444,18 @@ class _SettingsState extends State with WidgetsBindingObserver { ], ); } + + bool canStartOnBoot() { + // TODO need input + // start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW + if (_hasIgnoreBattery && !_ignoreBatteryOpt) { + return false; + } + if (!_systemAlertWindow) { + return false; + } + return true; + } } void showServerSettings(OverlayDialogManager dialogManager) async {