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 {