diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml index 1759a1ac0..04b2ccc9a 100644 --- a/flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.carriez.flutter_hbb"> + 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 7ce7d3ecc..3cb3ae581 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 @@ -2,20 +2,26 @@ package com.carriez.flutter_hbb import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.media.AudioRecord import android.media.AudioRecord.READ_BLOCKING import android.media.MediaCodecList import android.media.MediaFormat +import android.net.Uri import android.os.Build import android.os.Handler import android.os.Looper -import android.util.Log +import android.os.PowerManager +import android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS +import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat.getSystemService import com.hjq.permissions.Permission import com.hjq.permissions.XXPermissions import java.nio.ByteBuffer import java.util.* + @SuppressLint("ConstantLocale") val LOCAL_NAME = Locale.getDefault().toString() val SCREEN_INFO = Info(0, 0, 1, 200) @@ -38,8 +44,19 @@ fun testVP9Support(): Boolean { return res != null } +@RequiresApi(Build.VERSION_CODES.M) fun requestPermission(context: Context, type: String) { val permission = when (type) { + "ignore_battery_optimizations" -> { + try { + context.startActivity(Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { + data = Uri.parse("package:" + context.packageName) + }) + } catch (e:Exception) { + e.printStackTrace() + } + return + } "audio" -> { Permission.RECORD_AUDIO } @@ -52,7 +69,7 @@ fun requestPermission(context: Context, type: String) { } XXPermissions.with(context) .permission(permission) - .request { permissions, all -> + .request { _, all -> if (all) { Handler(Looper.getMainLooper()).post { MainActivity.flutterMethodChannel.invokeMethod( @@ -64,8 +81,13 @@ fun requestPermission(context: Context, type: String) { } } +@RequiresApi(Build.VERSION_CODES.M) fun checkPermission(context: Context, type: String): Boolean { val permission = when (type) { + "ignore_battery_optimizations" -> { + val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager + return pw.isIgnoringBatteryOptimizations(context.packageName) + } "audio" -> { Permission.RECORD_AUDIO } diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index d6be51986..1b36c548b 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -260,7 +260,7 @@ class PermissionManager { static Timer? _timer; static var _current = ""; - static final permissions = ["audio", "file"]; + static final permissions = ["audio", "file", "ignore_battery_optimizations"]; static bool isWaitingFile() { if (_completer != null) { @@ -279,9 +279,12 @@ class PermissionManager { if (!permissions.contains(type)) return Future.error("Wrong permission!$type"); + FFI.invokeMethod("request_permission", type); + if (type == "ignore_battery_optimizations") { + return Future.value(false); + } _current = type; _completer = Completer(); - FFI.invokeMethod("request_permission", type); // timeout _timer?.cancel(); diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 2c8b7fe9a..5bbc8dc96 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:settings_ui/settings_ui.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -26,11 +28,69 @@ class SettingsPage extends StatefulWidget implements PageShape { class _SettingsState extends State { static const url = 'https://rustdesk.com/'; + var _showIgnoreBattery = false; + + @override + void initState() { + super.initState(); + if (androidVersion >= 26) { + () async { + final res = + await PermissionManager.check("ignore_battery_optimizations"); + if (_showIgnoreBattery != !res) { + setState(() { + _showIgnoreBattery = !res; + }); + } + }(); + } + } @override Widget build(BuildContext context) { Provider.of(context); final username = getUsername(); + final settingsTiles = [ + SettingsTile.navigation( + title: Text(translate('ID/Relay Server')), + leading: Icon(Icons.cloud), + onPressed: (context) { + showServerSettings(); + }, + ), + SettingsTile.navigation( + title: Text(translate('Enhancements')), + leading: Icon(Icons.tune), + onPressed: (context) {}, + ), + ]; + if (_showIgnoreBattery) { + settingsTiles.add(SettingsTile.navigation( + title: Text(translate('Keep RustDesk background service')), + description: Text('* ${translate('Ignore Battery Optimizations')}'), + leading: Icon(Icons.settings_backup_restore), + onPressed: (context) { + PermissionManager.request("ignore_battery_optimizations"); + var count = 0; + Timer.periodic(Duration(seconds: 1), (timer) async { + debugPrint("BatteryOpt Timer, count:$count"); + if (count > 5) { + count = 0; + timer.cancel(); + } + if (await PermissionManager.check( + "ignore_battery_optimizations")) { + count = 0; + timer.cancel(); + setState(() { + _showIgnoreBattery = false; + }); + } + count++; + }); + })); + } + return SettingsList( sections: [ SettingsSection( @@ -53,15 +113,7 @@ class _SettingsState extends State { ), SettingsSection( title: Text(translate("Settings")), - tiles: [ - SettingsTile.navigation( - title: Text(translate('ID/Relay Server')), - leading: Icon(Icons.cloud), - onPressed: (context) { - showServerSettings(); - }, - ), - ], + tiles: settingsTiles, ), SettingsSection( title: Text(translate("About")), diff --git a/src/common.rs b/src/common.rs index 5ef127f46..b40c1a686 100644 --- a/src/common.rs +++ b/src/common.rs @@ -316,6 +316,9 @@ async fn test_nat_type_() -> ResultType { } pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec, bool) { + #[cfg(any(target_os = "android", target_os = "ios"))] + let (mut a, mut b) = get_rendezvous_server_(ms_timeout); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (mut a, mut b) = get_rendezvous_server_(ms_timeout).await; let mut b: Vec = b .drain(..) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ba8e6d178..ba85d7f39 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "进入隐私模式"), ("Out privacy mode", "退出隐私模式"), ("Language", "语言"), + ("Keep RustDesk background service", "保持RustDesk后台服务"), + ("Ignore Battery Optimizations", "忽略电池优化"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3c92ee71b..e12bc6075 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "v režimu soukromí"), ("Out privacy mode", "mimo režim soukromí"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 16b98359b..f8c676376 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "I databeskyttelsestilstand"), ("Out privacy mode", "Databeskyttelsestilstand fra"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index c2fd56002..8685e764d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "im Datenschutzmodus"), ("Out privacy mode", "Datenschutzmodus aus"), ("Language", "Sprache"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index c9701d63a..5952f51a1 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", ""), ("Out privacy mode", ""), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 60cc05341..9e12ed430 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "En modo de privacidad"), ("Out privacy mode", "Fuera del modo de privacidad"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index f447c5acd..5e223b262 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "en mode privé"), ("Out privacy mode", "hors mode de confidentialité"), ("Language", "Langue"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index e7a95870e..ab890766e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -270,7 +270,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Overwrite", "Felülírás"), ("This file exists, skip or overwrite this file?", "Ez a fájl már létezik, skippeljünk, vagy felülírjuk ezt a fájlt?"), ("Quit", "Kilépés"), - ("doc_mac_permission", "https://rustdesk.com/docs/hu/manual/mac/#enable-permissions"), + ("doc_mac_permission", "https://rustdesk.com/docs/hu/manual/mac/#enable-permissions"), ("Help", "Segítség"), ("Failed", "Sikertelen"), ("Succeeded", "Sikeres"), @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Belépés a privát módba"), ("Out privacy mode", "Kilépés a privát módból"), ("Language", "Nyelv"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index b1c6aaa1e..e926ee4cf 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Dalam mode privasi"), ("Out privacy mode", "Keluar dari mode privasi"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index c756d2218..016363d4e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "In modalità privacy"), ("Out privacy mode", "Fuori modalità privacy"), ("Language", "Linguaggio"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 946e47d4c..e36672e5c 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "No modo de privacidade"), ("Out privacy mode", "Fora do modo de privacidade"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d421bd85e..d8f138223 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "В режиме конфиденциальности"), ("Out privacy mode", "Выход из режима конфиденциальности"), ("Language", "Язык"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 8c60abaa9..4f2a4a29f 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "V režime súkromia"), ("Out privacy mode", "Mimo režimu súkromia"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ad963b323..8feac4d3b 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", ""), ("Out privacy mode", ""), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index d12b2881e..5bab8d8c9 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Gizlilik modunda"), ("Out privacy mode", "Gizlilik modu dışında"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 4f9813943..8bc1392bb 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "開啟隱私模式"), ("Out privacy mode", "退出隱私模式"), ("Language", "語言"), + ("Keep RustDesk background service", "保持RustDesk後台服務"), + ("Ignore Battery Optimizations", "忽略電池優化"), ].iter().cloned().collect(); } diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index efad1f9d9..87ae1cf33 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -171,7 +171,7 @@ impl VideoQoS { #[cfg(target_os = "android")] { // fix when andorid screen shrinks - let fix = Display::fix_quality() as u32; + let fix = scrap::Display::fix_quality() as u32; log::debug!("Android screen, fix quality:{}", fix); let base_bitrate = base_bitrate * fix; self.target_bitrate = base_bitrate * self.current_image_quality / 100;