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/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt
index 76068eee5..905a2734d 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt
@@ -8,14 +8,14 @@ package com.carriez.flutter_hbb
import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
-import android.content.Context
import android.graphics.Path
import android.os.Build
import android.util.Log
import android.view.accessibility.AccessibilityEvent
-import androidx.annotation.Keep
import androidx.annotation.RequiresApi
import java.util.*
+import kotlin.math.abs
+import kotlin.math.max
const val LIFT_DOWN = 9
const val LIFT_MOVE = 8
@@ -49,28 +49,40 @@ class InputService : AccessibilityService() {
private val wheelActionsQueue = LinkedList()
private var isWheelActionsPolling = false
+ private var isWaitingLongPress = false
@RequiresApi(Build.VERSION_CODES.N)
fun onMouseInput(mask: Int, _x: Int, _y: Int) {
- val x = if (_x < 0) {
- 0
- } else {
- _x
- }
-
- val y = if (_y < 0) {
- 0
- } else {
- _y
- }
+ val x = max(0, _x)
+ val y = max(0, _y)
if (mask == 0 || mask == LIFT_MOVE) {
+ val oldX = mouseX
+ val oldY = mouseY
mouseX = x * SCREEN_INFO.scale
mouseY = y * SCREEN_INFO.scale
+ if (isWaitingLongPress) {
+ val delta = abs(oldX - mouseX) + abs(oldY - mouseY)
+ Log.d(logTag,"delta:$delta")
+ if (delta > 8) {
+ isWaitingLongPress = false
+ }
+ }
}
// left button down ,was up
if (mask == LIFT_DOWN) {
+ isWaitingLongPress = true
+ timer.schedule(object : TimerTask() {
+ override fun run() {
+ if (isWaitingLongPress) {
+ isWaitingLongPress = false
+ leftIsDown = false
+ endGesture(mouseX, mouseY)
+ }
+ }
+ }, LONG_TAP_DELAY * 4)
+
leftIsDown = true
startGesture(mouseX, mouseY)
return
@@ -83,9 +95,12 @@ class InputService : AccessibilityService() {
// left up ,was down
if (mask == LIFT_UP) {
- leftIsDown = false
- endGesture(mouseX, mouseY)
- return
+ if (leftIsDown) {
+ leftIsDown = false
+ isWaitingLongPress = false
+ endGesture(mouseX, mouseY)
+ return
+ }
}
if (mask == RIGHT_UP) {
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/remote_page.dart b/flutter/lib/pages/remote_page.dart
index c383bc361..7a3e489b0 100644
--- a/flutter/lib/pages/remote_page.dart
+++ b/flutter/lib/pages/remote_page.dart
@@ -262,7 +262,6 @@ class _RemotePageState extends State {
: SafeArea(child:
OrientationBuilder(builder: (ctx, orientation) {
if (_currentOrientation != orientation) {
- debugPrint("on orientation changed");
Timer(Duration(milliseconds: 200), () {
resetMobileActionsOverlay();
_currentOrientation = orientation;
@@ -1061,6 +1060,8 @@ void showOptions() {
getRadio('Optimize reaction time', 'low', quality, setQuality),
Divider(color: MyTheme.border),
getToggle(setState, 'show-remote-cursor', 'Show remote cursor'),
+ getToggle(
+ setState, 'show-quality-monitor', 'Show quality monitor'),
] +
more),
actions: [],
diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart
index 2c8b7fe9a..7506d849f 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,75 @@ 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 enableAbr = FFI.getByName("option", "enable-abr") != 'N';
+ final enhancementsTiles = [
+ SettingsTile.switchTile(
+ leading: Icon(Icons.more_horiz),
+ title: Text(translate('Adaptive Bitrate') + '(beta)'),
+ initialValue: enableAbr,
+ onToggle: (v) {
+ final msg = Map()
+ ..["name"] = "enable-abr"
+ ..["value"] = "";
+ if (!v) {
+ msg["value"] = "N";
+ }
+ FFI.setByName("option", json.encode(msg));
+ setState(() {});
+ },
+ )
+ ];
+ if (_showIgnoreBattery) {
+ enhancementsTiles.insert(
+ 0,
+ SettingsTile.navigation(
+ title: Text(translate('Keep RustDesk background service')),
+ description:
+ Text('* ${translate('Ignore Battery Optimizations')}'),
+ leading: Icon(Icons.battery_saver),
+ onPressed: (context) {
+ PermissionManager.request("ignore_battery_optimizations");
+ var count = 0;
+ Timer.periodic(Duration(seconds: 1), (timer) async {
+ 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(
@@ -51,17 +117,17 @@ class _SettingsState extends State {
),
],
),
- SettingsSection(
- title: Text(translate("Settings")),
- tiles: [
- SettingsTile.navigation(
+ SettingsSection(title: Text(translate("Settings")), tiles: [
+ SettingsTile.navigation(
title: Text(translate('ID/Relay Server')),
leading: Icon(Icons.cloud),
onPressed: (context) {
showServerSettings();
- },
- ),
- ],
+ })
+ ]),
+ SettingsSection(
+ title: Text(translate("Enhancements")),
+ tiles: enhancementsTiles,
),
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..b0e06bc03 100644
--- a/src/server/video_qos.rs
+++ b/src/server/video_qos.rs
@@ -147,7 +147,6 @@ impl VideoQoS {
// handle image_quality change from peer
pub fn update_image_quality(&mut self, image_quality: i32) {
let image_quality = Self::convert_quality(image_quality) as _;
- log::debug!("VideoQoS update_image_quality: {}", image_quality);
if self.current_image_quality != image_quality {
self.current_image_quality = image_quality;
let _ = self.generate_bitrate().ok();
@@ -171,7 +170,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;