fix: Android, try sync clipboard on connecting (#10218)
* fix: Android, try sync clipboard on connecting Signed-off-by: fufesou <linlong1266@gmail.com> * Android, clipboard, more clear skip check Signed-off-by: fufesou <linlong1266@gmail.com> * comments Signed-off-by: fufesou <linlong1266@gmail.com> * comment todo: Android clipboard listener, callback twice Signed-off-by: fufesou <linlong1266@gmail.com> * Android, clipboard, remove listner Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
parent
8f44787ba3
commit
3c838e7a92
@ -306,7 +306,7 @@ class FloatingWindowService : Service(), View.OnTouchListener {
|
|||||||
popupMenu.menu.add(0, idShowRustDesk, 0, translate("Show RustDesk"))
|
popupMenu.menu.add(0, idShowRustDesk, 0, translate("Show RustDesk"))
|
||||||
// For host side, clipboard sync
|
// For host side, clipboard sync
|
||||||
val idSyncClipboard = 1
|
val idSyncClipboard = 1
|
||||||
val isClipboardListenerEnabled = MainActivity.rdClipboardManager?.isListening ?: false
|
val isClipboardListenerEnabled = MainActivity.rdClipboardManager?.isCaptureStarted ?: false
|
||||||
if (isClipboardListenerEnabled) {
|
if (isClipboardListenerEnabled) {
|
||||||
popupMenu.menu.add(0, idSyncClipboard, 0, translate("Update client clipboard"))
|
popupMenu.menu.add(0, idSyncClipboard, 0, translate("Update client clipboard"))
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
mainService?.let {
|
mainService?.let {
|
||||||
unbindService(serviceConnection)
|
unbindService(serviceConnection)
|
||||||
}
|
}
|
||||||
rdClipboardManager?.rustEnableServiceClipboard(false)
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +220,10 @@ class MainActivity : FlutterActivity() {
|
|||||||
result.success(true)
|
result.success(true)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
"try_sync_clipboard" -> {
|
||||||
|
rdClipboardManager?.syncClipboard(true)
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
GET_START_ON_BOOT_OPT -> {
|
GET_START_ON_BOOT_OPT -> {
|
||||||
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||||
result.success(prefs.getBoolean(KEY_START_ON_BOOT_OPT, false))
|
result.success(prefs.getBoolean(KEY_START_ON_BOOT_OPT, false))
|
||||||
@ -402,13 +405,4 @@ class MainActivity : FlutterActivity() {
|
|||||||
super.onStart()
|
super.onStart()
|
||||||
stopService(Intent(this, FloatingWindowService::class.java))
|
stopService(Intent(this, FloatingWindowService::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
// For client side
|
|
||||||
// When swithing from other app to this app, try to sync clipboard.
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
||||||
super.onWindowFocusChanged(hasFocus)
|
|
||||||
if (hasFocus) {
|
|
||||||
rdClipboardManager?.syncClipboard(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -433,6 +433,7 @@ class MainService : Service() {
|
|||||||
checkMediaPermission()
|
checkMediaPermission()
|
||||||
_isStart = true
|
_isStart = true
|
||||||
FFI.setFrameRawEnable("video",true)
|
FFI.setFrameRawEnable("video",true)
|
||||||
|
MainActivity.rdClipboardManager?.setCaptureStarted(_isStart)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,6 +442,7 @@ class MainService : Service() {
|
|||||||
Log.d(logTag, "Stop Capture")
|
Log.d(logTag, "Stop Capture")
|
||||||
FFI.setFrameRawEnable("video",false)
|
FFI.setFrameRawEnable("video",false)
|
||||||
_isStart = false
|
_isStart = false
|
||||||
|
MainActivity.rdClipboardManager?.setCaptureStarted(_isStart)
|
||||||
// release video
|
// release video
|
||||||
if (reuseVirtualDisplay) {
|
if (reuseVirtualDisplay) {
|
||||||
// The virtual display video projection can be paused by calling `setSurface(null)`.
|
// The virtual display video projection can be paused by calling `setSurface(null)`.
|
||||||
|
@ -36,19 +36,19 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) {
|
|||||||
// though the `lastUpdatedClipData` will be set to null once.
|
// though the `lastUpdatedClipData` will be set to null once.
|
||||||
private var lastUpdatedClipData: ClipData? = null
|
private var lastUpdatedClipData: ClipData? = null
|
||||||
private var isClientEnabled = true;
|
private var isClientEnabled = true;
|
||||||
private var _isListening = false;
|
private var _isCaptureStarted = false;
|
||||||
val isListening: Boolean
|
|
||||||
get() = _isListening
|
|
||||||
|
|
||||||
fun checkPrimaryClip(isClient: Boolean, isSync: Boolean) {
|
val isCaptureStarted: Boolean
|
||||||
|
get() = _isCaptureStarted
|
||||||
|
|
||||||
|
fun checkPrimaryClip(isClient: Boolean) {
|
||||||
val clipData = clipboardManager.primaryClip
|
val clipData = clipboardManager.primaryClip
|
||||||
if (clipData != null && clipData.itemCount > 0) {
|
if (clipData != null && clipData.itemCount > 0) {
|
||||||
// Only handle the first item in the clipboard for now.
|
// Only handle the first item in the clipboard for now.
|
||||||
val clip = clipData.getItemAt(0)
|
val clip = clipData.getItemAt(0)
|
||||||
val isHostSync = !isClient && isSync
|
// Ignore the `isClipboardDataEqual()` check if it's a host operation.
|
||||||
// Ignore the `isClipboardDataEqual()` check if it's a host sync operation.
|
// Because it's an action manually triggered by the user.
|
||||||
// Because it's a action manually triggered by the user.
|
if (isClient) {
|
||||||
if (!isHostSync) {
|
|
||||||
if (lastUpdatedClipData != null && isClipboardDataEqual(clipData, lastUpdatedClipData!!)) {
|
if (lastUpdatedClipData != null && isClipboardDataEqual(clipData, lastUpdatedClipData!!)) {
|
||||||
Log.d(logTag, "Clipboard data is the same as last update, ignore")
|
Log.d(logTag, "Clipboard data is the same as last update, ignore")
|
||||||
return
|
return
|
||||||
@ -95,13 +95,6 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val clipboardListener = object : ClipboardManager.OnPrimaryClipChangedListener {
|
|
||||||
override fun onPrimaryClipChanged() {
|
|
||||||
Log.d(logTag, "onPrimaryClipChanged")
|
|
||||||
checkPrimaryClip(true, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSupportedMimeType(mimeType: String): Boolean {
|
private fun isSupportedMimeType(mimeType: String): Boolean {
|
||||||
return supportedMimeTypes.contains(mimeType)
|
return supportedMimeTypes.contains(mimeType)
|
||||||
}
|
}
|
||||||
@ -136,43 +129,23 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
fun setCaptureStarted(started: Boolean) {
|
||||||
fun rustEnableServiceClipboard(enable: Boolean) {
|
_isCaptureStarted = started
|
||||||
Log.d(logTag, "rustEnableServiceClipboard: enable: $enable, _isListening: $_isListening")
|
|
||||||
if (enable) {
|
|
||||||
if (!_isListening) {
|
|
||||||
clipboardManager.addPrimaryClipChangedListener(clipboardListener)
|
|
||||||
_isListening = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_isListening) {
|
|
||||||
clipboardManager.removePrimaryClipChangedListener(clipboardListener)
|
|
||||||
_isListening = false
|
|
||||||
lastUpdatedClipData = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
fun rustEnableClientClipboard(enable: Boolean) {
|
fun rustEnableClientClipboard(enable: Boolean) {
|
||||||
Log.d(logTag, "rustEnableClientClipboard: enable: $enable")
|
Log.d(logTag, "rustEnableClientClipboard: enable: $enable")
|
||||||
isClientEnabled = enable
|
isClientEnabled = enable
|
||||||
if (enable) {
|
lastUpdatedClipData = null
|
||||||
lastUpdatedClipData = clipboardManager.primaryClip
|
|
||||||
} else {
|
|
||||||
lastUpdatedClipData = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun syncClipboard(isClient: Boolean) {
|
fun syncClipboard(isClient: Boolean) {
|
||||||
Log.d(logTag, "syncClipboard: isClient: $isClient, isClientEnabled: $isClientEnabled, _isListening: $_isListening")
|
Log.d(logTag, "syncClipboard: isClient: $isClient, isClientEnabled: $isClientEnabled")
|
||||||
if (isClient && !isClientEnabled) {
|
if (isClient && !isClientEnabled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!isClient && !_isListening) {
|
checkPrimaryClip(isClient)
|
||||||
return
|
|
||||||
}
|
|
||||||
checkPrimaryClip(isClient, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
|
@ -147,6 +147,19 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
gFFI.chatModel.onVoiceCallClosed("End connetion");
|
gFFI.chatModel.onVoiceCallClosed("End connetion");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
if (state == AppLifecycleState.resumed) {
|
||||||
|
trySyncClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For client side
|
||||||
|
// When swithing from other app to this app, try to sync clipboard.
|
||||||
|
void trySyncClipboard() {
|
||||||
|
gFFI.invokeMethod("try_sync_clipboard");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeMetrics() {
|
void didChangeMetrics() {
|
||||||
// If the soft keyboard is visible and the canvas has been changed(panned or scaled)
|
// If the soft keyboard is visible and the canvas has been changed(panned or scaled)
|
||||||
|
@ -597,8 +597,6 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
|||||||
style: const TextStyle(color: MyTheme.darkGray),
|
style: const TextStyle(color: MyTheme.darkGray),
|
||||||
))
|
))
|
||||||
]),
|
]),
|
||||||
PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
|
|
||||||
serverModel.toggleClipboard),
|
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ class ServerModel with ChangeNotifier {
|
|||||||
bool _inputOk = false;
|
bool _inputOk = false;
|
||||||
bool _audioOk = false;
|
bool _audioOk = false;
|
||||||
bool _fileOk = false;
|
bool _fileOk = false;
|
||||||
bool _clipboardOk = false;
|
|
||||||
bool _showElevation = false;
|
bool _showElevation = false;
|
||||||
bool hideCm = false;
|
bool hideCm = false;
|
||||||
int _connectStatus = 0; // Rendezvous Server status
|
int _connectStatus = 0; // Rendezvous Server status
|
||||||
@ -60,8 +59,6 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
bool get fileOk => _fileOk;
|
bool get fileOk => _fileOk;
|
||||||
|
|
||||||
bool get clipboardOk => _clipboardOk;
|
|
||||||
|
|
||||||
bool get showElevation => _showElevation;
|
bool get showElevation => _showElevation;
|
||||||
|
|
||||||
int get connectStatus => _connectStatus;
|
int get connectStatus => _connectStatus;
|
||||||
@ -212,10 +209,6 @@ class ServerModel with ChangeNotifier {
|
|||||||
_fileOk = fileOption != 'N';
|
_fileOk = fileOption != 'N';
|
||||||
}
|
}
|
||||||
|
|
||||||
// clipboard
|
|
||||||
final clipOption = await bind.mainGetOption(key: kOptionEnableClipboard);
|
|
||||||
_clipboardOk = clipOption != 'N';
|
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,14 +315,6 @@ class ServerModel with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleClipboard() async {
|
|
||||||
_clipboardOk = !_clipboardOk;
|
|
||||||
bind.mainSetOption(
|
|
||||||
key: kOptionEnableClipboard,
|
|
||||||
value: _clipboardOk ? defaultOptionYes : 'N');
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleInput() async {
|
toggleInput() async {
|
||||||
if (clients.isNotEmpty) {
|
if (clients.isNotEmpty) {
|
||||||
await showClientsMayNotBeChangedAlert(parent.target);
|
await showClientsMayNotBeChangedAlert(parent.target);
|
||||||
|
@ -371,14 +371,6 @@ pub fn call_clipboard_manager_update_clipboard(data: &[u8]) -> JniResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_clipboard_manager_enable_service_clipboard(enable: bool) -> JniResult<()> {
|
|
||||||
_call_clipboard_manager(
|
|
||||||
"rustEnableServiceClipboard",
|
|
||||||
"(Z)V",
|
|
||||||
&[JValue::Bool(jboolean::from(enable))],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call_clipboard_manager_enable_client_clipboard(enable: bool) -> JniResult<()> {
|
pub fn call_clipboard_manager_enable_client_clipboard(enable: bool) -> JniResult<()> {
|
||||||
_call_clipboard_manager(
|
_call_clipboard_manager(
|
||||||
"rustEnableClientClipboard",
|
"rustEnableClientClipboard",
|
||||||
|
@ -831,17 +831,6 @@ pub fn main_show_option(_key: String) -> SyncReturn<bool> {
|
|||||||
SyncReturn(false)
|
SyncReturn(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
fn enable_server_clipboard(keyboard_enabled: &str, clip_enabled: &str) {
|
|
||||||
use scrap::android::ffi::call_clipboard_manager_enable_service_clipboard;
|
|
||||||
let keyboard_enabled =
|
|
||||||
config::option2bool(config::keys::OPTION_ENABLE_KEYBOARD, &keyboard_enabled);
|
|
||||||
let clip_enabled = config::option2bool(config::keys::OPTION_ENABLE_CLIPBOARD, &clip_enabled);
|
|
||||||
crate::ui_cm_interface::switch_permission_all("clipboard".to_owned(), clip_enabled);
|
|
||||||
let _ = call_clipboard_manager_enable_service_clipboard(keyboard_enabled && clip_enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main_set_option(key: String, value: String) {
|
pub fn main_set_option(key: String, value: String) {
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
|
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
|
||||||
@ -849,11 +838,6 @@ pub fn main_set_option(key: String, value: String) {
|
|||||||
config::keys::OPTION_ENABLE_KEYBOARD,
|
config::keys::OPTION_ENABLE_KEYBOARD,
|
||||||
&value,
|
&value,
|
||||||
));
|
));
|
||||||
enable_server_clipboard(&value, &get_option(config::keys::OPTION_ENABLE_CLIPBOARD));
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
if key.eq(config::keys::OPTION_ENABLE_CLIPBOARD) {
|
|
||||||
enable_server_clipboard(&get_option(config::keys::OPTION_ENABLE_KEYBOARD), &value);
|
|
||||||
}
|
}
|
||||||
if key.eq("custom-rendezvous-server") {
|
if key.eq("custom-rendezvous-server") {
|
||||||
set_option(key, value.clone());
|
set_option(key, value.clone());
|
||||||
|
@ -8,8 +8,6 @@ use crate::ipc::{self, ClipboardFile, ClipboardNonFile, Data};
|
|||||||
use clipboard_master::{CallbackResult, ClipboardHandler};
|
use clipboard_master::{CallbackResult, ClipboardHandler};
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use hbb_common::config::{keys, option2bool};
|
use hbb_common::config::{keys, option2bool};
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
use scrap::android::ffi::call_clipboard_manager_enable_service_clipboard;
|
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
sync::mpsc::{channel, RecvTimeoutError, Sender},
|
sync::mpsc::{channel, RecvTimeoutError, Sender},
|
||||||
@ -224,24 +222,13 @@ impl Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
fn is_clipboard_enabled() -> bool {
|
|
||||||
let keyboard_enabled = crate::ui_interface::get_option(keys::OPTION_ENABLE_KEYBOARD);
|
|
||||||
let keyboard_enabled = option2bool(keys::OPTION_ENABLE_KEYBOARD, &keyboard_enabled);
|
|
||||||
let clip_enabled = crate::ui_interface::get_option(keys::OPTION_ENABLE_CLIPBOARD);
|
|
||||||
let clip_enabled = option2bool(keys::OPTION_ENABLE_CLIPBOARD, &clip_enabled);
|
|
||||||
keyboard_enabled && clip_enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
|
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
|
||||||
let _res = call_clipboard_manager_enable_service_clipboard(is_clipboard_enabled());
|
|
||||||
while sp.ok() {
|
while sp.ok() {
|
||||||
if let Some(msg) = crate::clipboard::get_clipboards_msg(false) {
|
if let Some(msg) = crate::clipboard::get_clipboards_msg(false) {
|
||||||
sp.send(msg);
|
sp.send(msg);
|
||||||
}
|
}
|
||||||
std::thread::sleep(Duration::from_millis(INTERVAL));
|
std::thread::sleep(Duration::from_millis(INTERVAL));
|
||||||
}
|
}
|
||||||
let _res = call_clipboard_manager_enable_service_clipboard(false);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1372,8 +1372,14 @@ impl Connection {
|
|||||||
if !self.follow_remote_window {
|
if !self.follow_remote_window {
|
||||||
noperms.push(NAME_WINDOW_FOCUS);
|
noperms.push(NAME_WINDOW_FOCUS);
|
||||||
}
|
}
|
||||||
if !self.clipboard_enabled()
|
// Do not consider the clipboard and keyboard permissions on Android.
|
||||||
|| !self.peer_keyboard_enabled()
|
// Because syncing the clipboard on Android is manually triggered by the user in the floating ball.
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let keyboard_clip_noperm = self.disable_keyboard || self.disable_clipboard;
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
let keyboard_clip_noperm =
|
||||||
|
!self.clipboard_enabled() || !self.peer_keyboard_enabled();
|
||||||
|
if keyboard_clip_noperm
|
||||||
|| crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) == "Y"
|
|| crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) == "Y"
|
||||||
{
|
{
|
||||||
noperms.push(super::clipboard_service::NAME);
|
noperms.push(super::clipboard_service::NAME);
|
||||||
|
@ -312,17 +312,6 @@ pub fn switch_permission(id: i32, name: String, enabled: bool) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
pub fn switch_permission_all(name: String, enabled: bool) {
|
|
||||||
for (_, client) in CLIENTS.read().unwrap().iter() {
|
|
||||||
allow_err!(client.tx.send(Data::SwitchPermission {
|
|
||||||
name: name.clone(),
|
|
||||||
enabled
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_clients_state() -> String {
|
pub fn get_clients_state() -> String {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user