Merge pull request #1218 from Heap-Hop/flutter_desktop
refactor flutter_desktop
This commit is contained in:
commit
927991c9de
@ -352,6 +352,23 @@ RadioListTile<T> getRadio<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckboxListTile getToggle(
|
||||||
|
String id, void Function(void Function()) setState, option, name) {
|
||||||
|
final opt = bind.getSessionToggleOptionSync(id: id, arg: option);
|
||||||
|
return CheckboxListTile(
|
||||||
|
value: opt,
|
||||||
|
onChanged: (v) {
|
||||||
|
setState(() {
|
||||||
|
bind.sessionToggleOption(id: id, value: option);
|
||||||
|
});
|
||||||
|
if (option == "show-quality-monitor") {
|
||||||
|
gFFI.qualityMonitorModel.checkShowQualityMonitor(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dense: true,
|
||||||
|
title: Text(translate(name)));
|
||||||
|
}
|
||||||
|
|
||||||
/// find ffi, tag is Remote ID
|
/// find ffi, tag is Remote ID
|
||||||
/// for session specific usage
|
/// for session specific usage
|
||||||
FFI ffi(String? tag) {
|
FFI ffi(String? tag) {
|
||||||
@ -374,3 +391,10 @@ Future<void> initGlobalFFI() async {
|
|||||||
// global shared preference
|
// global shared preference
|
||||||
await Get.putAsync(() => SharedPreferences.getInstance());
|
await Get.putAsync(() => SharedPreferences.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String translate(String name) {
|
||||||
|
if (name.startsWith('Failed to') && name.contains(': ')) {
|
||||||
|
return name.split(': ').map((x) => translate(x)).join(': ');
|
||||||
|
}
|
||||||
|
return platformFFI.translate(name, localeName);
|
||||||
|
}
|
||||||
|
@ -44,13 +44,22 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
|
|
||||||
/// Update url. If it's not null, means an update is available.
|
/// Update url. If it's not null, means an update is available.
|
||||||
var _updateUrl = '';
|
var _updateUrl = '';
|
||||||
var _menuPos;
|
|
||||||
|
|
||||||
Timer? _updateTimer;
|
Timer? _updateTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
if (_idController.text.isEmpty) {
|
||||||
|
() async {
|
||||||
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
|
if (lastRemoteId != _idController.text) {
|
||||||
|
setState(() {
|
||||||
|
_idController.text = lastRemoteId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||||
updateStatus();
|
updateStatus();
|
||||||
});
|
});
|
||||||
@ -58,9 +67,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<FfiModel>(context);
|
|
||||||
|
|
||||||
if (_idController.text.isEmpty) _idController.text = gFFI.getId();
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(color: isDarkTheme() ? null : MyTheme.grayBg),
|
decoration: BoxDecoration(color: isDarkTheme() ? null : MyTheme.grayBg),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -430,7 +436,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateStatus() async {
|
updateStatus() async {
|
||||||
svcStopped.value = gFFI.getOption("stop-service") == "Y";
|
svcStopped.value = bind.mainGetOption(key: "stop-service") == "Y";
|
||||||
final status =
|
final status =
|
||||||
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
|
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
|
||||||
svcStatusCode.value = status["status_num"];
|
svcStatusCode.value = status["status_num"];
|
||||||
@ -446,7 +452,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Widget> buildAddressBook(BuildContext context) async {
|
Future<Widget> buildAddressBook(BuildContext context) async {
|
||||||
final token = await gFFI.getLocalOption('access_token');
|
final token = await bind.mainGetLocalOption(key: 'access_token');
|
||||||
if (token.trim().isEmpty) {
|
if (token.trim().isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@ -819,10 +825,34 @@ class WebMenu extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _WebMenuState extends State<WebMenu> {
|
class _WebMenuState extends State<WebMenu> {
|
||||||
|
String? username;
|
||||||
|
String url = "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
() async {
|
||||||
|
final usernameRes = await getUsername();
|
||||||
|
final urlRes = await getUrl();
|
||||||
|
var update = false;
|
||||||
|
if (usernameRes != username) {
|
||||||
|
username = usernameRes;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
if (urlRes != url) {
|
||||||
|
url = urlRes;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<FfiModel>(context);
|
Provider.of<FfiModel>(context);
|
||||||
final username = getUsername();
|
|
||||||
return PopupMenuButton<String>(
|
return PopupMenuButton<String>(
|
||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
@ -840,7 +870,7 @@ class _WebMenuState extends State<WebMenu> {
|
|||||||
value: "server",
|
value: "server",
|
||||||
)
|
)
|
||||||
] +
|
] +
|
||||||
(getUrl().contains('admin.rustdesk.com')
|
(url.contains('admin.rustdesk.com')
|
||||||
? <PopupMenuItem<String>>[]
|
? <PopupMenuItem<String>>[]
|
||||||
: [
|
: [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
|
@ -7,7 +7,6 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/connection_page.dart';
|
import 'package:flutter_hbb/desktop/pages/connection_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/titlebar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/titlebar_widget.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
import 'package:flutter_hbb/models/server_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -156,6 +155,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
},
|
},
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final userName = await gFFI.userModel.getUserName();
|
final userName = await gFFI.userModel.getUserName();
|
||||||
|
final enabledInput = await bind.mainGetOption(key: 'enable-audio');
|
||||||
|
final defaultInput = await gFFI.getDefaultAudioInput();
|
||||||
var menu = <PopupMenuEntry>[
|
var menu = <PopupMenuEntry>[
|
||||||
genEnablePopupMenuItem(
|
genEnablePopupMenuItem(
|
||||||
translate("Enable Keyboard/Mouse"),
|
translate("Enable Keyboard/Mouse"),
|
||||||
@ -173,7 +174,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
translate("Enable TCP Tunneling"),
|
translate("Enable TCP Tunneling"),
|
||||||
'enable-tunnel',
|
'enable-tunnel',
|
||||||
),
|
),
|
||||||
genAudioInputPopupMenuItem(),
|
genAudioInputPopupMenuItem(enabledInput != "N", defaultInput),
|
||||||
PopupMenuDivider(),
|
PopupMenuDivider(),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(translate("ID/Relay Server")),
|
child: Text(translate("ID/Relay Server")),
|
||||||
@ -274,9 +275,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.refresh),
|
icon: Icon(Icons.refresh),
|
||||||
onPressed: () {
|
onPressed: () => bind.mainUpdateTemporaryPassword(),
|
||||||
gFFI.setByName("temporary_password");
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
FutureBuilder<Widget>(
|
FutureBuilder<Widget>(
|
||||||
future: buildPasswordPopupMenu(context),
|
future: buildPasswordPopupMenu(context),
|
||||||
@ -359,7 +358,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
if (gFFI.serverModel.temporaryPasswordLength !=
|
if (gFFI.serverModel.temporaryPasswordLength !=
|
||||||
e) {
|
e) {
|
||||||
gFFI.serverModel.temporaryPasswordLength = e;
|
gFFI.serverModel.temporaryPasswordLength = e;
|
||||||
gFFI.setByName("temporary_password");
|
bind.mainUpdateTemporaryPassword();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@ -465,49 +464,60 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
Get.find<SharedPreferences>().setString("darkTheme", choice);
|
Get.find<SharedPreferences>().setString("darkTheme", choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSelectMenu(String value) {
|
void onSelectMenu(String key) async {
|
||||||
if (value.startsWith('enable-')) {
|
if (key.startsWith('enable-')) {
|
||||||
final option = gFFI.getOption(value);
|
final option = await bind.mainGetOption(key: key);
|
||||||
gFFI.setOption(value, option == "N" ? "" : "N");
|
bind.mainSetOption(key: key, value: option == "N" ? "" : "N");
|
||||||
} else if (value.startsWith('allow-')) {
|
} else if (key.startsWith('allow-')) {
|
||||||
final option = gFFI.getOption(value);
|
final option = await bind.mainGetOption(key: key);
|
||||||
final choice = option == "Y" ? "" : "Y";
|
final choice = option == "Y" ? "" : "Y";
|
||||||
gFFI.setOption(value, choice);
|
bind.mainSetOption(key: key, value: choice);
|
||||||
changeTheme(choice);
|
changeTheme(choice);
|
||||||
} else if (value == "stop-service") {
|
} else if (key == "stop-service") {
|
||||||
final option = gFFI.getOption(value);
|
final option = await bind.mainGetOption(key: key);
|
||||||
gFFI.setOption(value, option == "Y" ? "" : "Y");
|
bind.mainSetOption(key: key, value: option == "Y" ? "" : "Y");
|
||||||
} else if (value == "change-id") {
|
} else if (key == "change-id") {
|
||||||
changeId();
|
changeId();
|
||||||
} else if (value == "custom-server") {
|
} else if (key == "custom-server") {
|
||||||
changeServer();
|
changeServer();
|
||||||
} else if (value == "whitelist") {
|
} else if (key == "whitelist") {
|
||||||
changeWhiteList();
|
changeWhiteList();
|
||||||
} else if (value == "socks5-proxy") {
|
} else if (key == "socks5-proxy") {
|
||||||
changeSocks5Proxy();
|
changeSocks5Proxy();
|
||||||
} else if (value == "about") {
|
} else if (key == "about") {
|
||||||
about();
|
about();
|
||||||
} else if (value == "logout") {
|
} else if (key == "logout") {
|
||||||
logOut();
|
logOut();
|
||||||
} else if (value == "login") {
|
} else if (key == "login") {
|
||||||
login();
|
login();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupMenuItem<String> genEnablePopupMenuItem(String label, String value) {
|
PopupMenuItem<String> genEnablePopupMenuItem(String label, String key) {
|
||||||
final v = gFFI.getOption(value);
|
Future<bool> getOptionEnable(String key) async {
|
||||||
final isEnable = value.startsWith('enable-') ? v != "N" : v == "Y";
|
final v = await bind.mainGetOption(key: key);
|
||||||
|
return key.startsWith('enable-') ? v != "N" : v == "Y";
|
||||||
|
}
|
||||||
|
|
||||||
return PopupMenuItem(
|
return PopupMenuItem(
|
||||||
child: Row(
|
child: FutureBuilder<bool>(
|
||||||
children: [
|
future: getOptionEnable(key),
|
||||||
Offstage(offstage: !isEnable, child: Icon(Icons.check)),
|
builder: (context, snapshot) {
|
||||||
Text(
|
var enable = false;
|
||||||
label,
|
if (snapshot.hasData && snapshot.data!) {
|
||||||
style: genTextStyle(isEnable),
|
enable = true;
|
||||||
),
|
}
|
||||||
],
|
return Row(
|
||||||
),
|
children: [
|
||||||
value: value,
|
Offstage(offstage: !enable, child: Icon(Icons.check)),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: genTextStyle(enable),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
value: key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,10 +528,11 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
color: Colors.redAccent, decoration: TextDecoration.lineThrough);
|
color: Colors.redAccent, decoration: TextDecoration.lineThrough);
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupMenuItem<String> genAudioInputPopupMenuItem() {
|
PopupMenuItem<String> genAudioInputPopupMenuItem(
|
||||||
final _enabledInput = gFFI.getOption('enable-audio');
|
bool enableInput, String defaultAudioInput) {
|
||||||
var defaultInput = gFFI.getDefaultAudioInput().obs;
|
final defaultInput = defaultAudioInput.obs;
|
||||||
var enabled = (_enabledInput != "N").obs;
|
final enabled = enableInput.obs;
|
||||||
|
|
||||||
return PopupMenuItem(
|
return PopupMenuItem(
|
||||||
child: FutureBuilder<List<String>>(
|
child: FutureBuilder<List<String>>(
|
||||||
future: gFFI.getAudioInputs(),
|
future: gFFI.getAudioInputs(),
|
||||||
@ -569,12 +580,13 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(translate("Audio Input"))),
|
child: Text(translate("Audio Input"))),
|
||||||
itemBuilder: (context) => inputList,
|
itemBuilder: (context) => inputList,
|
||||||
onSelected: (dev) {
|
onSelected: (dev) async {
|
||||||
if (dev == "Mute") {
|
if (dev == "Mute") {
|
||||||
gFFI.setOption(
|
await bind.mainSetOption(
|
||||||
'enable-audio', _enabledInput == 'N' ? '' : 'N');
|
key: 'enable-audio', value: enabled.value ? '' : 'N');
|
||||||
enabled.value = gFFI.getOption('enable-audio') != 'N';
|
enabled.value =
|
||||||
} else if (dev != gFFI.getDefaultAudioInput()) {
|
await bind.mainGetOption(key: 'enable-audio') != 'N';
|
||||||
|
} else if (dev != await gFFI.getDefaultAudioInput()) {
|
||||||
gFFI.setDefaultAudioInput(dev);
|
gFFI.setDefaultAudioInput(dev);
|
||||||
defaultInput.value = dev;
|
defaultInput.value = dev;
|
||||||
}
|
}
|
||||||
@ -1322,8 +1334,8 @@ Future<bool> loginDialog() async {
|
|||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPasswordDialog() {
|
void setPasswordDialog() async {
|
||||||
final pw = gFFI.getByName("permanent_password");
|
final pw = await bind.mainGetPermanentPassword();
|
||||||
final p0 = TextEditingController(text: pw);
|
final p0 = TextEditingController(text: pw);
|
||||||
final p1 = TextEditingController(text: pw);
|
final p1 = TextEditingController(text: pw);
|
||||||
var errMsg0 = "";
|
var errMsg0 = "";
|
||||||
@ -1413,7 +1425,7 @@ void setPasswordDialog() {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gFFI.setByName("permanent_password", pass);
|
bind.mainSetPermanentPassword(password: pass);
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
child: Text(translate("OK"))),
|
child: Text(translate("OK"))),
|
||||||
|
@ -346,8 +346,9 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
if (dy > 0)
|
if (dy > 0)
|
||||||
dy = -1;
|
dy = -1;
|
||||||
else if (dy < 0) dy = 1;
|
else if (dy < 0) dy = 1;
|
||||||
_ffi.setByName('send_mouse',
|
bind.sessionSendMouse(
|
||||||
'{"id": "${widget.id}", "type": "wheel", "x": "$dx", "y": "$dy"}');
|
id: widget.id,
|
||||||
|
msg: '{"type": "wheel", "x": "$dx", "y": "$dy"}');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Consumer<FfiModel>(
|
child: Consumer<FfiModel>(
|
||||||
@ -896,32 +897,6 @@ class ImagePainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckboxListTile getToggle(
|
|
||||||
String id, void Function(void Function()) setState, option, name) {
|
|
||||||
final opt = bind.getSessionToggleOptionSync(id: id, arg: option);
|
|
||||||
return CheckboxListTile(
|
|
||||||
value: opt,
|
|
||||||
onChanged: (v) {
|
|
||||||
setState(() {
|
|
||||||
bind.sessionToggleOption(id: id, value: option);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
dense: true,
|
|
||||||
title: Text(translate(name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
RadioListTile<String> getRadio(String name, String toValue, String curValue,
|
|
||||||
void Function(String?) onChange) {
|
|
||||||
return RadioListTile<String>(
|
|
||||||
controlAffinity: ListTileControlAffinity.trailing,
|
|
||||||
title: Text(translate(name)),
|
|
||||||
value: toValue,
|
|
||||||
groupValue: curValue,
|
|
||||||
onChanged: onChange,
|
|
||||||
dense: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showOptions(String id) async {
|
void showOptions(String id) async {
|
||||||
String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced';
|
String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced';
|
||||||
if (quality == '') quality = 'balanced';
|
if (quality == '') quality = 'balanced';
|
||||||
|
@ -89,7 +89,8 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: FutureBuilder<String>(
|
child: FutureBuilder<String>(
|
||||||
future: gFFI.getPeerOption(peer.id, 'alias'),
|
future: bind.mainGetPeerOption(
|
||||||
|
id: peer.id, key: 'alias'),
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final name = snapshot.data!.isEmpty
|
final name = snapshot.data!.isEmpty
|
||||||
@ -186,7 +187,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
elevation: 8,
|
elevation: 8,
|
||||||
);
|
);
|
||||||
if (value == 'remove') {
|
if (value == 'remove') {
|
||||||
setState(() => gFFI.setByName('remove', '$id'));
|
setState(() => bind.mainRemovePeer(id: id));
|
||||||
() async {
|
() async {
|
||||||
removePreference(id);
|
removePreference(id);
|
||||||
}();
|
}();
|
||||||
@ -304,7 +305,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
|
|
||||||
void _rename(String id) async {
|
void _rename(String id) async {
|
||||||
var isInProgress = false;
|
var isInProgress = false;
|
||||||
var name = await gFFI.getPeerOption(id, 'alias');
|
var name = await bind.mainGetPeerOption(id: id, key: 'alias');
|
||||||
if (widget.type == PeerType.ab) {
|
if (widget.type == PeerType.ab) {
|
||||||
final peer = gFFI.abModel.peers.firstWhere((p) => id == p['id']);
|
final peer = gFFI.abModel.peers.firstWhere((p) => id == p['id']);
|
||||||
if (peer == null) {
|
if (peer == null) {
|
||||||
@ -359,7 +360,8 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
if (k.currentState != null) {
|
if (k.currentState != null) {
|
||||||
if (k.currentState!.validate()) {
|
if (k.currentState!.validate()) {
|
||||||
k.currentState!.save();
|
k.currentState!.save();
|
||||||
await gFFI.setPeerOption(id, 'alias', name);
|
await bind.mainSetPeerOption(
|
||||||
|
id: id, key: 'alias', value: name);
|
||||||
if (widget.type == PeerType.ab) {
|
if (widget.type == PeerType.ab) {
|
||||||
gFFI.abModel.setPeerOption(id, 'alias', name);
|
gFFI.abModel.setPeerOption(id, 'alias', name);
|
||||||
await gFFI.abModel.updateAb();
|
await gFFI.abModel.updateAb();
|
||||||
|
@ -7,6 +7,8 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
|
import '../../models/peer_model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
import 'remote_page.dart';
|
import 'remote_page.dart';
|
||||||
import 'scan_page.dart';
|
import 'scan_page.dart';
|
||||||
@ -41,9 +43,20 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
if (_idController.text.isEmpty) {
|
||||||
|
() async {
|
||||||
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
|
if (lastRemoteId != _idController.text) {
|
||||||
|
setState(() {
|
||||||
|
_idController.text = lastRemoteId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
Timer(Duration(seconds: 5), () {
|
Timer(Duration(seconds: 5), () async {
|
||||||
_updateUrl = gFFI.getByName('software_update_url');
|
_updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
||||||
|
;
|
||||||
if (_updateUrl.isNotEmpty) setState(() {});
|
if (_updateUrl.isNotEmpty) setState(() {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -52,7 +65,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<FfiModel>(context);
|
Provider.of<FfiModel>(context);
|
||||||
if (_idController.text.isEmpty) _idController.text = gFFI.getId();
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@ -221,44 +233,52 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
final n = (windowWidth / (minWidth + 2 * space)).floor();
|
final n = (windowWidth / (minWidth + 2 * space)).floor();
|
||||||
width = windowWidth / n - 2 * space;
|
width = windowWidth / n - 2 * space;
|
||||||
}
|
}
|
||||||
final cards = <Widget>[];
|
return FutureBuilder<List<Peer>>(
|
||||||
var peers = gFFI.peers();
|
future: gFFI.peers(),
|
||||||
peers.forEach((p) {
|
builder: (context, snapshot) {
|
||||||
cards.add(Container(
|
final cards = <Widget>[];
|
||||||
width: width,
|
if (snapshot.hasData) {
|
||||||
child: Card(
|
final peers = snapshot.data!;
|
||||||
child: GestureDetector(
|
peers.forEach((p) {
|
||||||
onTap: !isWebDesktop ? () => connect('${p.id}') : null,
|
cards.add(Container(
|
||||||
onDoubleTap: isWebDesktop ? () => connect('${p.id}') : null,
|
width: width,
|
||||||
onLongPressStart: (details) {
|
child: Card(
|
||||||
final x = details.globalPosition.dx;
|
child: GestureDetector(
|
||||||
final y = details.globalPosition.dy;
|
onTap:
|
||||||
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
!isWebDesktop ? () => connect('${p.id}') : null,
|
||||||
showPeerMenu(context, p.id);
|
onDoubleTap:
|
||||||
},
|
isWebDesktop ? () => connect('${p.id}') : null,
|
||||||
child: ListTile(
|
onLongPressStart: (details) {
|
||||||
contentPadding: const EdgeInsets.only(left: 12),
|
final x = details.globalPosition.dx;
|
||||||
subtitle: Text('${p.username}@${p.hostname}'),
|
final y = details.globalPosition.dy;
|
||||||
title: Text('${p.id}'),
|
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||||
leading: Container(
|
showPeerMenu(context, p.id);
|
||||||
padding: const EdgeInsets.all(6),
|
},
|
||||||
child: getPlatformImage('${p.platform}'),
|
child: ListTile(
|
||||||
color: str2color('${p.id}${p.platform}', 0x7f)),
|
contentPadding: const EdgeInsets.only(left: 12),
|
||||||
trailing: InkWell(
|
subtitle: Text('${p.username}@${p.hostname}'),
|
||||||
child: Padding(
|
title: Text('${p.id}'),
|
||||||
padding: const EdgeInsets.all(12),
|
leading: Container(
|
||||||
child: Icon(Icons.more_vert)),
|
padding: const EdgeInsets.all(6),
|
||||||
onTapDown: (e) {
|
child: getPlatformImage('${p.platform}'),
|
||||||
final x = e.globalPosition.dx;
|
color: str2color('${p.id}${p.platform}', 0x7f)),
|
||||||
final y = e.globalPosition.dy;
|
trailing: InkWell(
|
||||||
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
child: Padding(
|
||||||
},
|
padding: const EdgeInsets.all(12),
|
||||||
onTap: () {
|
child: Icon(Icons.more_vert)),
|
||||||
showPeerMenu(context, p.id);
|
onTapDown: (e) {
|
||||||
}),
|
final x = e.globalPosition.dx;
|
||||||
)))));
|
final y = e.globalPosition.dy;
|
||||||
});
|
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||||
return Wrap(children: cards, spacing: space, runSpacing: space);
|
},
|
||||||
|
onTap: () {
|
||||||
|
showPeerMenu(context, p.id);
|
||||||
|
}),
|
||||||
|
)))));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Wrap(children: cards, spacing: space, runSpacing: space);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the peer menu and handle user's choice.
|
/// Show the peer menu and handle user's choice.
|
||||||
@ -280,7 +300,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
elevation: 8,
|
elevation: 8,
|
||||||
);
|
);
|
||||||
if (value == 'remove') {
|
if (value == 'remove') {
|
||||||
setState(() => gFFI.setByName('remove', '$id'));
|
setState(() => bind.mainRemovePeer(id: id));
|
||||||
() async {
|
() async {
|
||||||
removePreference(id);
|
removePreference(id);
|
||||||
}();
|
}();
|
||||||
@ -296,10 +316,34 @@ class WebMenu extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _WebMenuState extends State<WebMenu> {
|
class _WebMenuState extends State<WebMenu> {
|
||||||
|
String? username;
|
||||||
|
String url = "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
() async {
|
||||||
|
final usernameRes = await getUsername();
|
||||||
|
final urlRes = await getUrl();
|
||||||
|
var update = false;
|
||||||
|
if (usernameRes != username) {
|
||||||
|
username = usernameRes;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
if (urlRes != url) {
|
||||||
|
url = urlRes;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<FfiModel>(context);
|
Provider.of<FfiModel>(context);
|
||||||
final username = getUsername();
|
|
||||||
return PopupMenuButton<String>(
|
return PopupMenuButton<String>(
|
||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
@ -317,7 +361,7 @@ class _WebMenuState extends State<WebMenu> {
|
|||||||
value: "server",
|
value: "server",
|
||||||
)
|
)
|
||||||
] +
|
] +
|
||||||
(getUrl().contains('admin.rustdesk.com')
|
(url.contains('admin.rustdesk.com')
|
||||||
? <PopupMenuItem<String>>[]
|
? <PopupMenuItem<String>>[]
|
||||||
: [
|
: [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
|
@ -12,10 +12,10 @@ abstract class PageShape extends Widget {
|
|||||||
final List<Widget> appBarActions = [];
|
final List<Widget> appBarActions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
final homeKey = GlobalKey<_HomePageState>();
|
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
HomePage({Key? key}) : super(key: key);
|
static final homeKey = GlobalKey<_HomePageState>();
|
||||||
|
|
||||||
|
HomePage() : super(key: homeKey);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_HomePageState createState() => _HomePageState();
|
_HomePageState createState() => _HomePageState();
|
||||||
|
@ -12,6 +12,7 @@ import 'package:wakelock/wakelock.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
import '../widgets/gestures.dart';
|
import '../widgets/gestures.dart';
|
||||||
import '../widgets/overlay.dart';
|
import '../widgets/overlay.dart';
|
||||||
@ -135,7 +136,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
if (newValue.length > common) {
|
if (newValue.length > common) {
|
||||||
var s = newValue.substring(common);
|
var s = newValue.substring(common);
|
||||||
if (s.length > 1) {
|
if (s.length > 1) {
|
||||||
gFFI.setByName('input_string', s);
|
bind.sessionInputString(id: widget.id, value: s);
|
||||||
} else {
|
} else {
|
||||||
inputChar(s);
|
inputChar(s);
|
||||||
}
|
}
|
||||||
@ -169,11 +170,11 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
content == '()' ||
|
content == '()' ||
|
||||||
content == '【】')) {
|
content == '【】')) {
|
||||||
// can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input
|
// can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input
|
||||||
gFFI.setByName('input_string', content);
|
bind.sessionInputString(id: widget.id, value: content);
|
||||||
openKeyboard();
|
openKeyboard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gFFI.setByName('input_string', content);
|
bind.sessionInputString(id: widget.id, value: content);
|
||||||
} else {
|
} else {
|
||||||
inputChar(content);
|
inputChar(content);
|
||||||
}
|
}
|
||||||
@ -329,8 +330,9 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
if (dy > 0)
|
if (dy > 0)
|
||||||
dy = -1;
|
dy = -1;
|
||||||
else if (dy < 0) dy = 1;
|
else if (dy < 0) dy = 1;
|
||||||
gFFI.setByName(
|
bind.sessionSendMouse(
|
||||||
'send_mouse', '{"type": "wheel", "x": "$dx", "y": "$dy"}');
|
id: widget.id,
|
||||||
|
msg: '{"type": "wheel", "x": "$dx", "y": "$dy"}');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
@ -409,7 +411,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
icon: Icon(Icons.tv),
|
icon: Icon(Icons.tv),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => _showEdit = false);
|
setState(() => _showEdit = false);
|
||||||
showOptions();
|
showOptions(widget.id);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
] +
|
] +
|
||||||
@ -461,7 +463,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => _showEdit = false);
|
setState(() => _showEdit = false);
|
||||||
showActions();
|
showActions(widget.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@ -573,7 +575,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
},
|
},
|
||||||
onTwoFingerScaleEnd: (d) {
|
onTwoFingerScaleEnd: (d) {
|
||||||
_scale = 1;
|
_scale = 1;
|
||||||
gFFI.setByName('peer_option', '{"name": "view-style", "value": ""}');
|
bind.sessionPeerOption(id: widget.id, name: "view-style", value: "");
|
||||||
},
|
},
|
||||||
onThreeFingerVerticalDragUpdate: gFFI.ffiModel.isPeerAndroid
|
onThreeFingerVerticalDragUpdate: gFFI.ffiModel.isPeerAndroid
|
||||||
? null
|
? null
|
||||||
@ -620,8 +622,9 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
|
|
||||||
Widget getBodyForDesktopWithListener(bool keyboard) {
|
Widget getBodyForDesktopWithListener(bool keyboard) {
|
||||||
var paints = <Widget>[ImagePaint()];
|
var paints = <Widget>[ImagePaint()];
|
||||||
if (keyboard ||
|
final cursor = bind.getSessionToggleOptionSync(
|
||||||
gFFI.getByName('toggle_option', 'show-remote-cursor') == 'true') {
|
id: widget.id, arg: 'show-remote-cursor');
|
||||||
|
if (keyboard || cursor) {
|
||||||
paints.add(CursorPaint());
|
paints.add(CursorPaint());
|
||||||
}
|
}
|
||||||
return Container(
|
return Container(
|
||||||
@ -649,7 +652,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void showActions() {
|
void showActions(String id) async {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
final x = 120.0;
|
final x = 120.0;
|
||||||
final y = size.height;
|
final y = size.height;
|
||||||
@ -668,7 +671,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
showSetOSPassword(false);
|
showSetOSPassword(id, false);
|
||||||
},
|
},
|
||||||
child: Icon(Icons.edit, color: MyTheme.accent),
|
child: Icon(Icons.edit, color: MyTheme.accent),
|
||||||
)
|
)
|
||||||
@ -691,7 +694,8 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Text(translate('Insert Lock')), value: 'lock'));
|
child: Text(translate('Insert Lock')), value: 'lock'));
|
||||||
if (pi.platform == 'Windows' &&
|
if (pi.platform == 'Windows' &&
|
||||||
gFFI.getByName('toggle_option', 'privacy-mode') != 'true') {
|
await bind.getSessionToggleOption(id: id, arg: 'privacy-mode') !=
|
||||||
|
true) {
|
||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Text(translate((gFFI.ffiModel.inputBlocked ? 'Unb' : 'B') +
|
child: Text(translate((gFFI.ffiModel.inputBlocked ? 'Unb' : 'B') +
|
||||||
'lock user input')),
|
'lock user input')),
|
||||||
@ -713,28 +717,29 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
elevation: 8,
|
elevation: 8,
|
||||||
);
|
);
|
||||||
if (value == 'cad') {
|
if (value == 'cad') {
|
||||||
gFFI.setByName('ctrl_alt_del');
|
bind.sessionCtrlAltDel(id: widget.id);
|
||||||
} else if (value == 'lock') {
|
} else if (value == 'lock') {
|
||||||
gFFI.setByName('lock_screen');
|
bind.sessionLockScreen(id: widget.id);
|
||||||
} else if (value == 'block-input') {
|
} else if (value == 'block-input') {
|
||||||
gFFI.setByName('toggle_option',
|
bind.sessionToggleOption(
|
||||||
(gFFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
|
id: widget.id,
|
||||||
|
value: (gFFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
|
||||||
gFFI.ffiModel.inputBlocked = !gFFI.ffiModel.inputBlocked;
|
gFFI.ffiModel.inputBlocked = !gFFI.ffiModel.inputBlocked;
|
||||||
} else if (value == 'refresh') {
|
} else if (value == 'refresh') {
|
||||||
gFFI.setByName('refresh');
|
bind.sessionRefresh(id: widget.id);
|
||||||
} else if (value == 'paste') {
|
} else if (value == 'paste') {
|
||||||
() async {
|
() async {
|
||||||
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
|
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
|
||||||
if (data != null && data.text != null) {
|
if (data != null && data.text != null) {
|
||||||
gFFI.setByName('input_string', '${data.text}');
|
bind.sessionInputString(id: widget.id, value: data.text ?? "");
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
} else if (value == 'enter_os_password') {
|
} else if (value == 'enter_os_password') {
|
||||||
var password = gFFI.getByName('peer_option', "os-password");
|
var password = await bind.getSessionOption(id: id, arg: "os-password");
|
||||||
if (password != "") {
|
if (password != null) {
|
||||||
gFFI.setByName('input_os_password', password);
|
bind.sessionInputOsPassword(id: widget.id, value: password);
|
||||||
} else {
|
} else {
|
||||||
showSetOSPassword(true);
|
showSetOSPassword(id, true);
|
||||||
}
|
}
|
||||||
} else if (value == 'reset_canvas') {
|
} else if (value == 'reset_canvas') {
|
||||||
gFFI.cursorModel.reset();
|
gFFI.cursorModel.reset();
|
||||||
@ -762,8 +767,8 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
onTouchModeChange: (t) {
|
onTouchModeChange: (t) {
|
||||||
gFFI.ffiModel.toggleTouchMode();
|
gFFI.ffiModel.toggleTouchMode();
|
||||||
final v = gFFI.ffiModel.touchMode ? 'Y' : '';
|
final v = gFFI.ffiModel.touchMode ? 'Y' : '';
|
||||||
gFFI.setByName('peer_option',
|
bind.sessionPeerOption(
|
||||||
'{"name": "touch-mode", "value": "$v"}');
|
id: widget.id, name: "touch", value: v);
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -978,23 +983,23 @@ class QualityMonitor extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Speed: ${qualityMonitorModel.data.speed}",
|
"Speed: ${qualityMonitorModel.data.speed ?? ''}",
|
||||||
style: TextStyle(color: MyTheme.grayBg),
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"FPS: ${qualityMonitorModel.data.fps}",
|
"FPS: ${qualityMonitorModel.data.fps ?? ''}",
|
||||||
style: TextStyle(color: MyTheme.grayBg),
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Delay: ${qualityMonitorModel.data.delay} ms",
|
"Delay: ${qualityMonitorModel.data.delay ?? ''} ms",
|
||||||
style: TextStyle(color: MyTheme.grayBg),
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Target Bitrate: ${qualityMonitorModel.data.targetBitrate}kb",
|
"Target Bitrate: ${qualityMonitorModel.data.targetBitrate ?? ''}kb",
|
||||||
style: TextStyle(color: MyTheme.grayBg),
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Codec: ${qualityMonitorModel.data.codecFormat}",
|
"Codec: ${qualityMonitorModel.data.codecFormat ?? ''}",
|
||||||
style: TextStyle(color: MyTheme.grayBg),
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -1003,26 +1008,11 @@ class QualityMonitor extends StatelessWidget {
|
|||||||
: SizedBox.shrink())));
|
: SizedBox.shrink())));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckboxListTile getToggle(
|
void showOptions(String id) async {
|
||||||
void Function(void Function()) setState, option, name) {
|
String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced';
|
||||||
return CheckboxListTile(
|
|
||||||
value: gFFI.getByName('toggle_option', option) == 'true',
|
|
||||||
onChanged: (v) {
|
|
||||||
setState(() {
|
|
||||||
gFFI.setByName('toggle_option', option);
|
|
||||||
});
|
|
||||||
if (option == "show-quality-monitor") {
|
|
||||||
gFFI.qualityMonitorModel.checkShowQualityMonitor();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dense: true,
|
|
||||||
title: Text(translate(name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void showOptions() {
|
|
||||||
String quality = gFFI.getByName('image_quality');
|
|
||||||
if (quality == '') quality = 'balanced';
|
if (quality == '') quality = 'balanced';
|
||||||
String viewStyle = gFFI.getByName('peer_option', 'view-style');
|
String viewStyle =
|
||||||
|
await bind.getSessionOption(id: id, arg: 'view-style') ?? '';
|
||||||
var displays = <Widget>[];
|
var displays = <Widget>[];
|
||||||
final pi = gFFI.ffiModel.pi;
|
final pi = gFFI.ffiModel.pi;
|
||||||
final image = gFFI.ffiModel.getConnectionImage();
|
final image = gFFI.ffiModel.getConnectionImage();
|
||||||
@ -1035,7 +1025,7 @@ void showOptions() {
|
|||||||
children.add(InkWell(
|
children.add(InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (i == cur) return;
|
if (i == cur) return;
|
||||||
gFFI.setByName('switch_display', i.toString());
|
bind.sessionSwitchDisplay(id: id, value: i);
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
child: Ink(
|
child: Ink(
|
||||||
@ -1064,30 +1054,30 @@ void showOptions() {
|
|||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
final more = <Widget>[];
|
final more = <Widget>[];
|
||||||
if (perms['audio'] != false) {
|
if (perms['audio'] != false) {
|
||||||
more.add(getToggle(setState, 'disable-audio', 'Mute'));
|
more.add(getToggle(id, setState, 'disable-audio', 'Mute'));
|
||||||
}
|
}
|
||||||
if (perms['keyboard'] != false) {
|
if (perms['keyboard'] != false) {
|
||||||
if (perms['clipboard'] != false)
|
if (perms['clipboard'] != false)
|
||||||
more.add(getToggle(setState, 'disable-clipboard', 'Disable clipboard'));
|
more.add(
|
||||||
|
getToggle(id, setState, 'disable-clipboard', 'Disable clipboard'));
|
||||||
more.add(getToggle(
|
more.add(getToggle(
|
||||||
setState, 'lock-after-session-end', 'Lock after session end'));
|
id, setState, 'lock-after-session-end', 'Lock after session end'));
|
||||||
if (pi.platform == 'Windows') {
|
if (pi.platform == 'Windows') {
|
||||||
more.add(getToggle(setState, 'privacy-mode', 'Privacy mode'));
|
more.add(getToggle(id, setState, 'privacy-mode', 'Privacy mode'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var setQuality = (String? value) {
|
var setQuality = (String? value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
quality = value;
|
quality = value;
|
||||||
gFFI.setByName('image_quality', value);
|
bind.sessionSetImageQuality(id: id, value: value);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var setViewStyle = (String? value) {
|
var setViewStyle = (String? value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
viewStyle = value;
|
viewStyle = value;
|
||||||
gFFI.setByName(
|
bind.sessionPeerOption(id: id, name: "view-style", value: value);
|
||||||
'peer_option', '{"name": "view-style", "value": "$value"}');
|
|
||||||
gFFI.canvasModel.updateViewStyle();
|
gFFI.canvasModel.updateViewStyle();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -1105,9 +1095,10 @@ void showOptions() {
|
|||||||
getRadio('Balanced', 'balanced', quality, setQuality),
|
getRadio('Balanced', 'balanced', quality, setQuality),
|
||||||
getRadio('Optimize reaction time', 'low', quality, setQuality),
|
getRadio('Optimize reaction time', 'low', quality, setQuality),
|
||||||
Divider(color: MyTheme.border),
|
Divider(color: MyTheme.border),
|
||||||
getToggle(setState, 'show-remote-cursor', 'Show remote cursor'),
|
|
||||||
getToggle(
|
getToggle(
|
||||||
setState, 'show-quality-monitor', 'Show quality monitor'),
|
id, setState, 'show-remote-cursor', 'Show remote cursor'),
|
||||||
|
getToggle(id, setState, 'show-quality-monitor',
|
||||||
|
'Show quality monitor'),
|
||||||
] +
|
] +
|
||||||
more),
|
more),
|
||||||
actions: [],
|
actions: [],
|
||||||
@ -1134,13 +1125,13 @@ void showRestartRemoteDevice(PeerInfo pi, String id) async {
|
|||||||
onPressed: () => close(true), child: Text(translate("OK"))),
|
onPressed: () => close(true), child: Text(translate("OK"))),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
if (res == true) gFFI.setByName('restart_remote_device');
|
if (res == true) bind.sessionRestartRemoteDevice(id: id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showSetOSPassword(bool login) {
|
void showSetOSPassword(String id, bool login) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var password = gFFI.getByName('peer_option', "os-password");
|
var password = await bind.getSessionOption(id: id, arg: "os-password") ?? "";
|
||||||
var autoLogin = gFFI.getByName('peer_option', "auto-login") != "";
|
var autoLogin = await bind.getSessionOption(id: id, arg: "auto-login") != "";
|
||||||
controller.text = password;
|
controller.text = password;
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
@ -1173,12 +1164,11 @@ void showSetOSPassword(bool login) {
|
|||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
var text = controller.text.trim();
|
var text = controller.text.trim();
|
||||||
gFFI.setByName(
|
bind.sessionPeerOption(id: id, name: "os-password", value: text);
|
||||||
'peer_option', '{"name": "os-password", "value": "$text"}');
|
bind.sessionPeerOption(
|
||||||
gFFI.setByName('peer_option',
|
id: id, name: "auto-login", value: autoLogin ? 'Y' : '');
|
||||||
'{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}');
|
|
||||||
if (text != "" && login) {
|
if (text != "" && login) {
|
||||||
gFFI.setByName('input_os_password', text);
|
bind.sessionInputOsPassword(id: id, value: text);
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart';
|
|||||||
import 'package:zxing2/qrcode.dart';
|
import 'package:zxing2/qrcode.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/platform_model.dart';
|
||||||
|
|
||||||
class ScanPage extends StatefulWidget {
|
class ScanPage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -153,54 +153,80 @@ class _ScanPageState extends State<ScanPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showServerSettingsWithValue(
|
void showServerSettingsWithValue(
|
||||||
String id, String relay, String key, String api) {
|
String id, String relay, String key, String api) async {
|
||||||
final formKey = GlobalKey<FormState>();
|
Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions());
|
||||||
final id0 = gFFI.getByName('option', 'custom-rendezvous-server');
|
String id0 = oldOptions['custom-rendezvous-server'] ?? "";
|
||||||
final relay0 = gFFI.getByName('option', 'relay-server');
|
String relay0 = oldOptions['relay-server'] ?? "";
|
||||||
final api0 = gFFI.getByName('option', 'api-server');
|
String api0 = oldOptions['api-server'] ?? "";
|
||||||
final key0 = gFFI.getByName('option', 'key');
|
String key0 = oldOptions['key'] ?? "";
|
||||||
|
var isInProgress = false;
|
||||||
|
final idController = TextEditingController(text: id);
|
||||||
|
final relayController = TextEditingController(text: relay);
|
||||||
|
final apiController = TextEditingController(text: api);
|
||||||
|
|
||||||
|
String? idServerMsg;
|
||||||
|
String? relayServerMsg;
|
||||||
|
String? apiServerMsg;
|
||||||
|
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
|
Future<bool> validate() async {
|
||||||
|
if (idController.text != id) {
|
||||||
|
final res = await validateAsync(idController.text);
|
||||||
|
setState(() => idServerMsg = res);
|
||||||
|
if (idServerMsg != null) return false;
|
||||||
|
id = idController.text;
|
||||||
|
}
|
||||||
|
if (relayController.text != relay) {
|
||||||
|
relayServerMsg = await validateAsync(relayController.text);
|
||||||
|
if (relayServerMsg != null) return false;
|
||||||
|
relay = relayController.text;
|
||||||
|
}
|
||||||
|
if (apiController.text != relay) {
|
||||||
|
apiServerMsg = await validateAsync(apiController.text);
|
||||||
|
if (apiServerMsg != null) return false;
|
||||||
|
api = apiController.text;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('ID/Relay Server')),
|
title: Text(translate('ID/Relay Server')),
|
||||||
content: Form(
|
content: Form(
|
||||||
key: formKey,
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: id,
|
controller: idController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: translate('ID Server'),
|
labelText: translate('ID Server'),
|
||||||
),
|
errorText: idServerMsg),
|
||||||
validator: validate,
|
|
||||||
onSaved: (String? value) {
|
|
||||||
if (value != null) id = value.trim();
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
] +
|
] +
|
||||||
(isAndroid
|
(isAndroid
|
||||||
? [
|
? [
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: relay,
|
controller: relayController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: translate('Relay Server'),
|
labelText: translate('Relay Server'),
|
||||||
),
|
errorText: relayServerMsg),
|
||||||
validator: validate,
|
|
||||||
onSaved: (String? value) {
|
|
||||||
if (value != null) relay = value.trim();
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
: []) +
|
: []) +
|
||||||
[
|
[
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: api,
|
controller: apiController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: translate('API Server'),
|
labelText: translate('API Server'),
|
||||||
),
|
),
|
||||||
validator: validate,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
onSaved: (String? value) {
|
validator: (v) {
|
||||||
if (value != null) api = value.trim();
|
if (v != null && v.length > 0) {
|
||||||
|
if (!(v.startsWith('http://') ||
|
||||||
|
v.startsWith("https://"))) {
|
||||||
|
return translate("invalid_http");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apiServerMsg;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
@ -208,11 +234,13 @@ void showServerSettingsWithValue(
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Key',
|
labelText: 'Key',
|
||||||
),
|
),
|
||||||
validator: null,
|
onChanged: (String? value) {
|
||||||
onSaved: (String? value) {
|
|
||||||
if (value != null) key = value.trim();
|
if (value != null) key = value.trim();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Offstage(
|
||||||
|
offstage: !isInProgress,
|
||||||
|
child: LinearProgressIndicator())
|
||||||
])),
|
])),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@ -224,24 +252,28 @@ void showServerSettingsWithValue(
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
if (formKey.currentState != null &&
|
setState(() {
|
||||||
formKey.currentState!.validate()) {
|
idServerMsg = null;
|
||||||
formKey.currentState!.save();
|
relayServerMsg = null;
|
||||||
if (id != id0)
|
apiServerMsg = null;
|
||||||
gFFI.setByName('option',
|
isInProgress = true;
|
||||||
'{"name": "custom-rendezvous-server", "value": "$id"}');
|
});
|
||||||
|
if (await validate()) {
|
||||||
|
if (id != id0) {
|
||||||
|
bind.mainSetOption(key: "custom-rendezvous-server", value: id);
|
||||||
|
}
|
||||||
if (relay != relay0)
|
if (relay != relay0)
|
||||||
gFFI.setByName(
|
bind.mainSetOption(key: "relay-server", value: relay);
|
||||||
'option', '{"name": "relay-server", "value": "$relay"}');
|
if (key != key0) bind.mainSetOption(key: "key", value: key);
|
||||||
if (key != key0)
|
|
||||||
gFFI.setByName('option', '{"name": "key", "value": "$key"}');
|
|
||||||
if (api != api0)
|
if (api != api0)
|
||||||
gFFI.setByName(
|
bind.mainSetOption(key: "api-server", value: api);
|
||||||
'option', '{"name": "api-server", "value": "$api"}');
|
|
||||||
gFFI.ffiModel.updateUser();
|
gFFI.ffiModel.updateUser();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
setState(() {
|
||||||
|
isInProgress = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
child: Text(translate('OK')),
|
child: Text(translate('OK')),
|
||||||
),
|
),
|
||||||
@ -250,11 +282,11 @@ void showServerSettingsWithValue(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
String? validate(value) {
|
Future<String?> validateAsync(String value) async {
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final res = gFFI.getByName('test_if_valid_server', value);
|
final res = await bind.mainTestIfValidServer(server: value);
|
||||||
return res.isEmpty ? null : res;
|
return res.isEmpty ? null : res;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
|
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../models/server_model.dart';
|
import '../../models/server_model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
|
|
||||||
@ -99,10 +96,7 @@ class ServerPage extends StatelessWidget implements PageShape {
|
|||||||
} else if (value == kUsePermanentPassword ||
|
} else if (value == kUsePermanentPassword ||
|
||||||
value == kUseTemporaryPassword ||
|
value == kUseTemporaryPassword ||
|
||||||
value == kUseBothPasswords) {
|
value == kUseBothPasswords) {
|
||||||
Map<String, String> msg = Map()
|
bind.mainSetOption(key: "verification-method", value: value);
|
||||||
..["name"] = "verification-method"
|
|
||||||
..["value"] = value;
|
|
||||||
gFFI.setByName('option', jsonEncode(msg));
|
|
||||||
gFFI.serverModel.updatePasswordModel();
|
gFFI.serverModel.updatePasswordModel();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -183,9 +177,8 @@ class ServerInfo extends StatelessWidget {
|
|||||||
? null
|
? null
|
||||||
: IconButton(
|
: IconButton(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
onPressed: () {
|
onPressed: () =>
|
||||||
gFFI.setByName("temporary_password");
|
bind.mainUpdateTemporaryPassword())),
|
||||||
})),
|
|
||||||
onSaved: (String? value) {},
|
onSaved: (String? value) {},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -406,8 +399,7 @@ class ConnectionManager extends StatelessWidget {
|
|||||||
MaterialStateProperty.all(Colors.red)),
|
MaterialStateProperty.all(Colors.red)),
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
gFFI.setByName(
|
bind.serverCloseConnection(connId: entry.key);
|
||||||
"close_conn", entry.key.toString());
|
|
||||||
gFFI.invokeMethod(
|
gFFI.invokeMethod(
|
||||||
"cancel_notification", entry.key);
|
"cancel_notification", entry.key);
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
import 'scan_page.dart';
|
import 'scan_page.dart';
|
||||||
@ -30,15 +31,38 @@ class SettingsPage extends StatefulWidget implements PageShape {
|
|||||||
const url = 'https://rustdesk.com/';
|
const url = 'https://rustdesk.com/';
|
||||||
final _hasIgnoreBattery = androidVersion >= 26;
|
final _hasIgnoreBattery = androidVersion >= 26;
|
||||||
var _ignoreBatteryOpt = false;
|
var _ignoreBatteryOpt = false;
|
||||||
|
var _enableAbr = false;
|
||||||
|
|
||||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||||
|
String? username;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
if (_hasIgnoreBattery) {
|
|
||||||
updateIgnoreBatteryStatus();
|
() async {
|
||||||
}
|
var update = false;
|
||||||
|
if (_hasIgnoreBattery) {
|
||||||
|
update = await updateIgnoreBatteryStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
final usernameRes = await getUsername();
|
||||||
|
if (usernameRes != username) {
|
||||||
|
update = true;
|
||||||
|
username = usernameRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
final enableAbrRes = await bind.mainGetOption(key: "enable-abr") != "N";
|
||||||
|
if (enableAbrRes != _enableAbr) {
|
||||||
|
update = true;
|
||||||
|
_enableAbr = enableAbrRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -50,16 +74,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
updateIgnoreBatteryStatus();
|
() async {
|
||||||
|
if (await updateIgnoreBatteryStatus()) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateIgnoreBatteryStatus() async {
|
Future<bool> updateIgnoreBatteryStatus() async {
|
||||||
final res = await PermissionManager.check("ignore_battery_optimizations");
|
final res = await PermissionManager.check("ignore_battery_optimizations");
|
||||||
if (_ignoreBatteryOpt != res) {
|
if (_ignoreBatteryOpt != res) {
|
||||||
setState(() {
|
_ignoreBatteryOpt = res;
|
||||||
_ignoreBatteryOpt = res;
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -69,21 +95,15 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<FfiModel>(context);
|
Provider.of<FfiModel>(context);
|
||||||
final username = getUsername();
|
|
||||||
final enableAbr = gFFI.getByName("option", "enable-abr") != 'N';
|
|
||||||
final enhancementsTiles = [
|
final enhancementsTiles = [
|
||||||
SettingsTile.switchTile(
|
SettingsTile.switchTile(
|
||||||
title: Text(translate('Adaptive Bitrate') + '(beta)'),
|
title: Text(translate('Adaptive Bitrate') + ' (beta)'),
|
||||||
initialValue: enableAbr,
|
initialValue: _enableAbr,
|
||||||
onToggle: (v) {
|
onToggle: (v) {
|
||||||
final msg = Map()
|
bind.mainSetOption(key: "enable-abr", value: v ? "" : "N");
|
||||||
..["name"] = "enable-abr"
|
setState(() {
|
||||||
..["value"] = "";
|
_enableAbr = !_enableAbr;
|
||||||
if (!v) {
|
});
|
||||||
msg["value"] = "N";
|
|
||||||
}
|
|
||||||
gFFI.setByName("option", json.encode(msg));
|
|
||||||
setState(() {});
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
@ -184,29 +204,27 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showServerSettings() {
|
void showServerSettings() async {
|
||||||
final id = gFFI.getByName('option', 'custom-rendezvous-server');
|
Map<String, dynamic> options = jsonDecode(await bind.mainGetOptions());
|
||||||
final relay = gFFI.getByName('option', 'relay-server');
|
String id = options['custom-rendezvous-server'] ?? "";
|
||||||
final api = gFFI.getByName('option', 'api-server');
|
String relay = options['relay-server'] ?? "";
|
||||||
final key = gFFI.getByName('option', 'key');
|
String api = options['api-server'] ?? "";
|
||||||
|
String key = options['key'] ?? "";
|
||||||
showServerSettingsWithValue(id, relay, key, api);
|
showServerSettingsWithValue(id, relay, key, api);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showLanguageSettings() {
|
void showLanguageSettings() async {
|
||||||
try {
|
try {
|
||||||
final langs = json.decode(gFFI.getByName('langs')) as List<dynamic>;
|
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;
|
||||||
var lang = gFFI.getByName('local_option', 'lang');
|
var lang = await bind.mainGetLocalOption(key: "lang");
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
final setLang = (v) {
|
final setLang = (v) {
|
||||||
if (lang != v) {
|
if (lang != v) {
|
||||||
setState(() {
|
setState(() {
|
||||||
lang = v;
|
lang = v;
|
||||||
});
|
});
|
||||||
final msg = Map()
|
bind.mainSetLocalOption(key: "lang", value: v);
|
||||||
..['name'] = 'lang'
|
HomePage.homeKey.currentState?.refreshPages();
|
||||||
..['value'] = v;
|
|
||||||
gFFI.setByName('local_option', json.encode(msg));
|
|
||||||
homeKey.currentState?.refreshPages();
|
|
||||||
Future.delayed(Duration(milliseconds: 200), close);
|
Future.delayed(Duration(milliseconds: 200), close);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -277,8 +295,8 @@ fetch('http://localhost:21114/api/login', {
|
|||||||
final body = {
|
final body = {
|
||||||
'username': name,
|
'username': name,
|
||||||
'password': pass,
|
'password': pass,
|
||||||
'id': gFFI.getByName('server_id'),
|
'id': bind.mainGetMyId(),
|
||||||
'uuid': gFFI.getByName('uuid')
|
'uuid': bind.mainGetUuid()
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
final response = await http.post(Uri.parse('$url/api/login'),
|
final response = await http.post(Uri.parse('$url/api/login'),
|
||||||
@ -298,26 +316,22 @@ String parseResp(String body) {
|
|||||||
}
|
}
|
||||||
final token = data['access_token'];
|
final token = data['access_token'];
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
gFFI.setByName('option', '{"name": "access_token", "value": "$token"}');
|
bind.mainSetOption(key: "access_token", value: token);
|
||||||
}
|
}
|
||||||
final info = data['user'];
|
final info = data['user'];
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
final value = json.encode(info);
|
final value = json.encode(info);
|
||||||
gFFI.setByName(
|
bind.mainSetOption(key: "user_info", value: value);
|
||||||
'option', json.encode({"name": "user_info", "value": value}));
|
|
||||||
gFFI.ffiModel.updateUser();
|
gFFI.ffiModel.updateUser();
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshCurrentUser() async {
|
void refreshCurrentUser() async {
|
||||||
final token = gFFI.getByName("option", "access_token");
|
final token = await bind.mainGetOption(key: "access_token");
|
||||||
if (token == '') return;
|
if (token == '') return;
|
||||||
final url = getUrl();
|
final url = getUrl();
|
||||||
final body = {
|
final body = {'id': bind.mainGetMyId(), 'uuid': bind.mainGetUuid()};
|
||||||
'id': gFFI.getByName('server_id'),
|
|
||||||
'uuid': gFFI.getByName('uuid')
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(Uri.parse('$url/api/currentUser'),
|
final response = await http.post(Uri.parse('$url/api/currentUser'),
|
||||||
headers: {
|
headers: {
|
||||||
@ -337,13 +351,10 @@ void refreshCurrentUser() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void logout() async {
|
void logout() async {
|
||||||
final token = gFFI.getByName("option", "access_token");
|
final token = await bind.mainGetOption(key: "access_token");
|
||||||
if (token == '') return;
|
if (token == '') return;
|
||||||
final url = getUrl();
|
final url = getUrl();
|
||||||
final body = {
|
final body = {'id': bind.mainGetMyId(), 'uuid': bind.mainGetUuid()};
|
||||||
'id': gFFI.getByName('server_id'),
|
|
||||||
'uuid': gFFI.getByName('uuid')
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
await http.post(Uri.parse('$url/api/logout'),
|
await http.post(Uri.parse('$url/api/logout'),
|
||||||
headers: {
|
headers: {
|
||||||
@ -357,16 +368,16 @@ void logout() async {
|
|||||||
resetToken();
|
resetToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetToken() {
|
void resetToken() async {
|
||||||
gFFI.setByName('option', '{"name": "access_token", "value": ""}');
|
await bind.mainSetOption(key: "access_token", value: "");
|
||||||
gFFI.setByName('option', '{"name": "user_info", "value": ""}');
|
await bind.mainSetOption(key: "user_info", value: "");
|
||||||
gFFI.ffiModel.updateUser();
|
gFFI.ffiModel.updateUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
String getUrl() {
|
Future<String> getUrl() async {
|
||||||
var url = gFFI.getByName('option', 'api-server');
|
var url = await bind.mainGetOption(key: "api-server");
|
||||||
if (url == '') {
|
if (url == '') {
|
||||||
url = gFFI.getByName('option', 'custom-rendezvous-server');
|
url = await bind.mainGetOption(key: "custom-rendezvous-server");
|
||||||
if (url != '') {
|
if (url != '') {
|
||||||
if (url.contains(':')) {
|
if (url.contains(':')) {
|
||||||
final tmp = url.split(':');
|
final tmp = url.split(':');
|
||||||
@ -455,11 +466,11 @@ void showLogin() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
String? getUsername() {
|
Future<String?> getUsername() async {
|
||||||
final token = gFFI.getByName("option", "access_token");
|
final token = await bind.mainGetOption(key: "access_token");
|
||||||
String? username;
|
String? username;
|
||||||
if (token != "") {
|
if (token != "") {
|
||||||
final info = gFFI.getByName("option", "user_info");
|
final info = await bind.mainGetOption(key: "user_info");
|
||||||
if (info != "") {
|
if (info != "") {
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> tmp = json.decode(info);
|
Map<String, dynamic> tmp = json.decode(info);
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/platform_model.dart';
|
||||||
|
|
||||||
void clientClose() {
|
void clientClose() {
|
||||||
msgBox('', 'Close', 'Are you sure to close the connection?');
|
msgBox('', 'Close', 'Are you sure to close the connection?');
|
||||||
@ -22,8 +20,8 @@ void showError({Duration duration = SEC1}) {
|
|||||||
showToast(translate("Error"), duration: SEC1);
|
showToast(translate("Error"), duration: SEC1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPermanentPasswordDialog() {
|
void setPermanentPasswordDialog() async {
|
||||||
final pw = gFFI.getByName("permanent_password");
|
final pw = await bind.mainGetPermanentPassword();
|
||||||
final p0 = TextEditingController(text: pw);
|
final p0 = TextEditingController(text: pw);
|
||||||
final p1 = TextEditingController(text: pw);
|
final p1 = TextEditingController(text: pw);
|
||||||
var validateLength = false;
|
var validateLength = false;
|
||||||
@ -103,9 +101,9 @@ void setPermanentPasswordDialog() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTemporaryPasswordLengthDialog() {
|
void setTemporaryPasswordLengthDialog() async {
|
||||||
List<String> lengths = ['6', '8', '10'];
|
List<String> lengths = ['6', '8', '10'];
|
||||||
String length = gFFI.getByName('option', 'temporary-password-length');
|
String length = await bind.mainGetOption(key: "temporary-password-length");
|
||||||
var index = lengths.indexOf(length);
|
var index = lengths.indexOf(length);
|
||||||
if (index < 0) index = 0;
|
if (index < 0) index = 0;
|
||||||
length = lengths[index];
|
length = lengths[index];
|
||||||
@ -116,11 +114,8 @@ void setTemporaryPasswordLengthDialog() {
|
|||||||
setState(() {
|
setState(() {
|
||||||
length = newValue;
|
length = newValue;
|
||||||
});
|
});
|
||||||
Map<String, String> msg = Map()
|
bind.mainSetOption(key: "temporary-password-length", value: newValue);
|
||||||
..["name"] = "temporary-password-length"
|
bind.mainUpdateTemporaryPassword();
|
||||||
..["value"] = newValue;
|
|
||||||
gFFI.setByName("option", jsonEncode(msg));
|
|
||||||
gFFI.setByName("temporary_password");
|
|
||||||
Future.delayed(Duration(milliseconds: 200), () {
|
Future.delayed(Duration(milliseconds: 200), () {
|
||||||
close();
|
close();
|
||||||
showSuccess();
|
showSuccess();
|
||||||
@ -138,9 +133,9 @@ void setTemporaryPasswordLengthDialog() {
|
|||||||
}, backDismiss: true, clickMaskDismiss: true);
|
}, backDismiss: true, clickMaskDismiss: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enterPasswordDialog(String id) {
|
void enterPasswordDialog(String id) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var remember = gFFI.getByName('remember', id) == 'true';
|
var remember = await bind.getSessionRemember(id: id) ?? false;
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('Password Required')),
|
title: Text(translate('Password Required')),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:dash_chat_2/dash_chat_2.dart';
|
import 'package:dash_chat_2/dash_chat_2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
|
|
||||||
import '../../mobile/widgets/overlay.dart';
|
import '../../mobile/widgets/overlay.dart';
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
@ -72,7 +71,7 @@ class ChatModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
receive(int id, String text) {
|
receive(int id, String text) async {
|
||||||
if (text.isEmpty) return;
|
if (text.isEmpty) return;
|
||||||
// first message show overlay icon
|
// first message show overlay icon
|
||||||
if (chatIconOverlayEntry == null) {
|
if (chatIconOverlayEntry == null) {
|
||||||
@ -82,7 +81,7 @@ class ChatModel with ChangeNotifier {
|
|||||||
if (id == clientModeID) {
|
if (id == clientModeID) {
|
||||||
chatUser = ChatUser(
|
chatUser = ChatUser(
|
||||||
firstName: _ffi.target?.ffiModel.pi.username,
|
firstName: _ffi.target?.ffiModel.pi.username,
|
||||||
id: _ffi.target?.getId() ?? "",
|
id: await bind.mainGetLastRemoteId(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final client = _ffi.target?.serverModel.clients[id];
|
final client = _ffi.target?.serverModel.clients[id];
|
||||||
@ -105,12 +104,11 @@ class ChatModel with ChangeNotifier {
|
|||||||
if (message.text.isNotEmpty) {
|
if (message.text.isNotEmpty) {
|
||||||
_messages[_currentID]?.insert(message);
|
_messages[_currentID]?.insert(message);
|
||||||
if (_currentID == clientModeID) {
|
if (_currentID == clientModeID) {
|
||||||
_ffi.target?.setByName("chat_client_mode", message.text);
|
if (_ffi.target != null) {
|
||||||
|
bind.sessionSendChat(id: _ffi.target!.id, text: message.text);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final msg = Map()
|
bind.serverSendChat(connId: _currentID, msg: message.text);
|
||||||
..["id"] = _currentID
|
|
||||||
..["text"] = message.text;
|
|
||||||
_ffi.target?.setByName("chat_server_mode", jsonEncode(msg));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -290,7 +290,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onReady() async {
|
onReady() async {
|
||||||
_localOption.home = _ffi.target?.getByName("get_home_dir") ?? "";
|
_localOption.home = await bind.mainGetHomeDir();
|
||||||
_localOption.showHidden = (await bind.sessionGetPeerOption(
|
_localOption.showHidden = (await bind.sessionGetPeerOption(
|
||||||
id: _ffi.target?.id ?? "", name: "local_show_hidden"))
|
id: _ffi.target?.id ?? "", name: "local_show_hidden"))
|
||||||
.isNotEmpty;
|
.isNotEmpty;
|
||||||
@ -444,7 +444,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
items.items.forEach((from) async {
|
items.items.forEach((from) async {
|
||||||
_jobId++;
|
_jobId++;
|
||||||
await bind.sessionSendFiles(
|
await bind.sessionSendFiles(
|
||||||
id: '${_ffi.target?.getId()}',
|
id: await bind.mainGetLastRemoteId(),
|
||||||
actId: _jobId,
|
actId: _jobId,
|
||||||
path: from.path,
|
path: from.path,
|
||||||
to: PathUtil.join(toPath, from.name, isWindows),
|
to: PathUtil.join(toPath, from.name, isWindows),
|
||||||
|
@ -171,6 +171,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
parent.target?.serverModel.onClientAuthorized(evt);
|
parent.target?.serverModel.onClientAuthorized(evt);
|
||||||
} else if (name == 'on_client_remove') {
|
} else if (name == 'on_client_remove') {
|
||||||
parent.target?.serverModel.onClientRemove(evt);
|
parent.target?.serverModel.onClientRemove(evt);
|
||||||
|
} else if (name == 'update_quality_status') {
|
||||||
|
parent.target?.qualityMonitorModel.updateQualityStatus(evt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -807,9 +809,10 @@ class QualityMonitorModel with ChangeNotifier {
|
|||||||
bool get show => _show;
|
bool get show => _show;
|
||||||
QualityMonitorData get data => _data;
|
QualityMonitorData get data => _data;
|
||||||
|
|
||||||
checkShowQualityMonitor() {
|
checkShowQualityMonitor(String id) async {
|
||||||
final show =
|
final show = await bind.getSessionToggleOption(
|
||||||
gFFI.getByName('toggle_option', 'show-quality-monitor') == 'true';
|
id: id, arg: 'show-quality-monitor') ==
|
||||||
|
true;
|
||||||
if (_show != show) {
|
if (_show != show) {
|
||||||
_show = show;
|
_show = show;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -878,11 +881,6 @@ class FFI {
|
|||||||
this.qualityMonitorModel = QualityMonitorModel(WeakReference(this));
|
this.qualityMonitorModel = QualityMonitorModel(WeakReference(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the remote id for current client.
|
|
||||||
String getId() {
|
|
||||||
return getByName('remote_id'); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a mouse tap event(down and up).
|
/// Send a mouse tap event(down and up).
|
||||||
void tap(MouseButtons button) {
|
void tap(MouseButtons button) {
|
||||||
sendMouse('down', button);
|
sendMouse('down', button);
|
||||||
@ -891,8 +889,10 @@ class FFI {
|
|||||||
|
|
||||||
/// Send scroll event with scroll distance [y].
|
/// Send scroll event with scroll distance [y].
|
||||||
void scroll(int y) {
|
void scroll(int y) {
|
||||||
setByName('send_mouse',
|
bind.sessionSendMouse(
|
||||||
json.encode(modify({'id': id, 'type': 'wheel', 'y': y.toString()})));
|
id: id,
|
||||||
|
msg: json
|
||||||
|
.encode(modify({'id': id, 'type': 'wheel', 'y': y.toString()})));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reconnect to the remote peer.
|
/// Reconnect to the remote peer.
|
||||||
@ -918,8 +918,9 @@ class FFI {
|
|||||||
/// Send mouse press event.
|
/// Send mouse press event.
|
||||||
void sendMouse(String type, MouseButtons button) {
|
void sendMouse(String type, MouseButtons button) {
|
||||||
if (!ffiModel.keyboard()) return;
|
if (!ffiModel.keyboard()) return;
|
||||||
setByName('send_mouse',
|
bind.sessionSendMouse(
|
||||||
json.encode(modify({'id': id, 'type': type, 'buttons': button.value})));
|
id: id,
|
||||||
|
msg: json.encode(modify({'type': type, 'buttons': button.value})));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send key stroke event.
|
/// Send key stroke event.
|
||||||
@ -955,14 +956,14 @@ class FFI {
|
|||||||
if (!ffiModel.keyboard()) return;
|
if (!ffiModel.keyboard()) return;
|
||||||
var x2 = x.toInt();
|
var x2 = x.toInt();
|
||||||
var y2 = y.toInt();
|
var y2 = y.toInt();
|
||||||
setByName(
|
bind.sessionSendMouse(
|
||||||
'send_mouse', json.encode(modify({'id': id, 'x': '$x2', 'y': '$y2'})));
|
id: id, msg: json.encode(modify({'x': '$x2', 'y': '$y2'})));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List the saved peers.
|
/// List the saved peers.
|
||||||
List<Peer> peers() {
|
Future<List<Peer>> peers() async {
|
||||||
try {
|
try {
|
||||||
var str = getByName('peers'); // TODO
|
var str = await bind.mainGetRecentPeers();
|
||||||
if (str == "") return [];
|
if (str == "") return [];
|
||||||
List<dynamic> peers = json.decode(str);
|
List<dynamic> peers = json.decode(str);
|
||||||
return peers
|
return peers
|
||||||
@ -1034,41 +1035,14 @@ class FFI {
|
|||||||
|
|
||||||
/// Send **get** command to the Rust core based on [name] and [arg].
|
/// Send **get** command to the Rust core based on [name] and [arg].
|
||||||
/// Return the result as a string.
|
/// Return the result as a string.
|
||||||
String getByName(String name, [String arg = '']) {
|
// String getByName(String name, [String arg = '']) {
|
||||||
return platformFFI.getByName(name, arg);
|
// return platformFFI.getByName(name, arg);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Send **set** command to the Rust core based on [name] and [value].
|
/// Send **set** command to the Rust core based on [name] and [value].
|
||||||
void setByName(String name, [String value = '']) {
|
// void setByName(String name, [String value = '']) {
|
||||||
platformFFI.setByName(name, value);
|
// platformFFI.setByName(name, value);
|
||||||
}
|
// }
|
||||||
|
|
||||||
String getOption(String name) {
|
|
||||||
return platformFFI.getByName("option", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> getLocalOption(String name) {
|
|
||||||
return bind.mainGetLocalOption(key: name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setLocalOption(String key, String value) {
|
|
||||||
return bind.mainSetLocalOption(key: key, value: value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> getPeerOption(String id, String key) {
|
|
||||||
return bind.mainGetPeerOption(id: id, key: key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setPeerOption(String id, String key, String value) {
|
|
||||||
return bind.mainSetPeerOption(id: id, key: key, value: value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOption(String name, String value) {
|
|
||||||
Map<String, String> res = Map()
|
|
||||||
..["name"] = name
|
|
||||||
..["value"] = value;
|
|
||||||
return platformFFI.setByName('option', jsonEncode(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouse(Map<String, dynamic> evt, {double tabBarHeight = 0.0}) {
|
handleMouse(Map<String, dynamic> evt, {double tabBarHeight = 0.0}) {
|
||||||
var type = '';
|
var type = '';
|
||||||
@ -1121,8 +1095,7 @@ class FFI {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
evt['buttons'] = buttons;
|
evt['buttons'] = buttons;
|
||||||
evt['id'] = id;
|
bind.sessionSendMouse(id: id, msg: json.encode(evt));
|
||||||
setByName('send_mouse', json.encode(evt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listenToMouse(bool yesOrNo) {
|
listenToMouse(bool yesOrNo) {
|
||||||
@ -1145,8 +1118,8 @@ class FFI {
|
|||||||
return await bind.mainGetSoundInputs();
|
return await bind.mainGetSoundInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
String getDefaultAudioInput() {
|
Future<String> getDefaultAudioInput() async {
|
||||||
final input = getOption('audio-input');
|
final input = await bind.mainGetOption(key: 'audio-input');
|
||||||
if (input.isEmpty && Platform.isWindows) {
|
if (input.isEmpty && Platform.isWindows) {
|
||||||
return "System Sound";
|
return "System Sound";
|
||||||
}
|
}
|
||||||
@ -1154,11 +1127,14 @@ class FFI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setDefaultAudioInput(String input) {
|
void setDefaultAudioInput(String input) {
|
||||||
setOption('audio-input', input);
|
bind.mainSetOption(key: 'audio-input', value: input);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, String>> getHttpHeaders() async {
|
Future<Map<String, String>> getHttpHeaders() async {
|
||||||
return {"Authorization": "Bearer " + await getLocalOption("access_token")};
|
return {
|
||||||
|
"Authorization":
|
||||||
|
"Bearer " + await bind.mainGetLocalOption(key: "access_token")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1230,11 +1206,12 @@ void initializeCursorAndCanvas(FFI ffi) async {
|
|||||||
/// Translate text based on the pre-defined dictionary.
|
/// Translate text based on the pre-defined dictionary.
|
||||||
/// note: params [FFI?] can be used to replace global FFI implementation
|
/// note: params [FFI?] can be used to replace global FFI implementation
|
||||||
/// for example: during global initialization, gFFI not exists yet.
|
/// for example: during global initialization, gFFI not exists yet.
|
||||||
String translate(String name, {FFI? ffi}) {
|
// String translate(String name, {FFI? ffi}) {
|
||||||
if (name.startsWith('Failed to') && name.contains(': ')) {
|
// if (name.startsWith('Failed to') && name.contains(': ')) {
|
||||||
return name.split(': ').map((x) => translate(x)).join(': ');
|
// return name.split(': ').map((x) => translate(x)).join(': ');
|
||||||
}
|
// }
|
||||||
var a = 'translate';
|
// var a = 'translate';
|
||||||
var b = '{"locale": "$localeName", "text": "$name"}';
|
// var b = '{"locale": "$localeName", "text": "$name"}';
|
||||||
return (ffi ?? gFFI).getByName(a, b);
|
//
|
||||||
}
|
// return (ffi ?? gFFI).getByName(a, b);
|
||||||
|
// }
|
||||||
|
@ -29,8 +29,7 @@ typedef HandleEvent = void Function(Map<String, dynamic> evt);
|
|||||||
class PlatformFFI {
|
class PlatformFFI {
|
||||||
String _dir = '';
|
String _dir = '';
|
||||||
String _homeDir = '';
|
String _homeDir = '';
|
||||||
F2? _getByName;
|
F2? _translate;
|
||||||
F3? _setByName;
|
|
||||||
var _eventHandlers = Map<String, Map<String, HandleEvent>>();
|
var _eventHandlers = Map<String, Map<String, HandleEvent>>();
|
||||||
late RustdeskImpl _ffiBind;
|
late RustdeskImpl _ffiBind;
|
||||||
late String _appType;
|
late String _appType;
|
||||||
@ -75,31 +74,19 @@ class PlatformFFI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send **get** command to the Rust core based on [name] and [arg].
|
String translate(String name, String locale) {
|
||||||
/// Return the result as a string.
|
if (_translate == null) return '';
|
||||||
String getByName(String name, [String arg = '']) {
|
|
||||||
if (_getByName == null) return '';
|
|
||||||
var a = name.toNativeUtf8();
|
var a = name.toNativeUtf8();
|
||||||
var b = arg.toNativeUtf8();
|
var b = locale.toNativeUtf8();
|
||||||
var p = _getByName!(a, b);
|
var p = _translate!(a, b);
|
||||||
assert(p != nullptr);
|
assert(p != nullptr);
|
||||||
var res = p.toDartString();
|
final res = p.toDartString();
|
||||||
calloc.free(p);
|
calloc.free(p);
|
||||||
calloc.free(a);
|
calloc.free(a);
|
||||||
calloc.free(b);
|
calloc.free(b);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send **set** command to the Rust core based on [name] and [value].
|
|
||||||
void setByName(String name, [String value = '']) {
|
|
||||||
if (_setByName == null) return;
|
|
||||||
var a = name.toNativeUtf8();
|
|
||||||
var b = value.toNativeUtf8();
|
|
||||||
_setByName!(a, b);
|
|
||||||
calloc.free(a);
|
|
||||||
calloc.free(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Init the FFI class, loads the native Rust core library.
|
/// Init the FFI class, loads the native Rust core library.
|
||||||
Future<Null> init(String appType) async {
|
Future<Null> init(String appType) async {
|
||||||
_appType = appType;
|
_appType = appType;
|
||||||
@ -118,10 +105,7 @@ class PlatformFFI {
|
|||||||
: DynamicLibrary.process();
|
: DynamicLibrary.process();
|
||||||
debugPrint('initializing FFI ${_appType}');
|
debugPrint('initializing FFI ${_appType}');
|
||||||
try {
|
try {
|
||||||
_getByName = dylib.lookupFunction<F2, F2>('get_by_name');
|
_translate = dylib.lookupFunction<F2, F2>('translate');
|
||||||
_setByName =
|
|
||||||
dylib.lookupFunction<Void Function(Pointer<Utf8>, Pointer<Utf8>), F3>(
|
|
||||||
'set_by_name');
|
|
||||||
_dir = (await getApplicationDocumentsDirectory()).path;
|
_dir = (await getApplicationDocumentsDirectory()).path;
|
||||||
_ffiBind = RustdeskImpl(dylib);
|
_ffiBind = RustdeskImpl(dylib);
|
||||||
_startListenEvent(_ffiBind); // global event
|
_startListenEvent(_ffiBind); // global event
|
||||||
@ -162,10 +146,10 @@ class PlatformFFI {
|
|||||||
}
|
}
|
||||||
print(
|
print(
|
||||||
"_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir");
|
"_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir");
|
||||||
setByName('info1', id);
|
await _ffiBind.mainDeviceId(id: id);
|
||||||
setByName('info2', name);
|
await _ffiBind.mainDeviceName(name: name);
|
||||||
setByName('home_dir', _homeDir);
|
await _ffiBind.mainSetHomeDir(home: _homeDir);
|
||||||
setByName('init', _dir);
|
await _ffiBind.mainInit(appDir: _dir);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:wakelock/wakelock.dart';
|
import 'package:wakelock/wakelock.dart';
|
||||||
|
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
@ -57,7 +58,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
set verificationMethod(String method) {
|
set verificationMethod(String method) {
|
||||||
_verificationMethod = method;
|
_verificationMethod = method;
|
||||||
gFFI.setOption("verification-method", method);
|
bind.mainSetOption(key: "verification-method", value: method);
|
||||||
}
|
}
|
||||||
|
|
||||||
String get temporaryPasswordLength {
|
String get temporaryPasswordLength {
|
||||||
@ -70,7 +71,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
set temporaryPasswordLength(String length) {
|
set temporaryPasswordLength(String length) {
|
||||||
_temporaryPasswordLength = length;
|
_temporaryPasswordLength = length;
|
||||||
gFFI.setOption("temporary-password-length", length);
|
bind.mainSetOption(key: "temporary-password-length", value: length);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditingController get serverId => _serverId;
|
TextEditingController get serverId => _serverId;
|
||||||
@ -85,7 +86,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
ServerModel(this.parent) {
|
ServerModel(this.parent) {
|
||||||
() async {
|
() async {
|
||||||
_emptyIdShow = translate("Generating ...", ffi: this.parent.target);
|
_emptyIdShow = translate("Generating ...");
|
||||||
_serverId = TextEditingController(text: this._emptyIdShow);
|
_serverId = TextEditingController(text: this._emptyIdShow);
|
||||||
/**
|
/**
|
||||||
* 1. check android permission
|
* 1. check android permission
|
||||||
@ -98,42 +99,29 @@ class ServerModel with ChangeNotifier {
|
|||||||
// audio
|
// audio
|
||||||
if (androidVersion < 30 || !await PermissionManager.check("audio")) {
|
if (androidVersion < 30 || !await PermissionManager.check("audio")) {
|
||||||
_audioOk = false;
|
_audioOk = false;
|
||||||
parent.target?.setByName(
|
bind.mainSetOption(key: "enable-audio", value: "N");
|
||||||
'option',
|
|
||||||
jsonEncode(Map()
|
|
||||||
..["name"] = "enable-audio"
|
|
||||||
..["value"] = "N"));
|
|
||||||
} else {
|
} else {
|
||||||
final audioOption = parent.target?.getByName('option', 'enable-audio');
|
final audioOption = await bind.mainGetOption(key: 'enable-audio');
|
||||||
_audioOk = audioOption?.isEmpty ?? false;
|
_audioOk = audioOption.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// file
|
// file
|
||||||
if (!await PermissionManager.check("file")) {
|
if (!await PermissionManager.check("file")) {
|
||||||
_fileOk = false;
|
_fileOk = false;
|
||||||
parent.target?.setByName(
|
bind.mainSetOption(key: "enable-file-transfer", value: "N");
|
||||||
'option',
|
|
||||||
jsonEncode(Map()
|
|
||||||
..["name"] = "enable-file-transfer"
|
|
||||||
..["value"] = "N"));
|
|
||||||
} else {
|
} else {
|
||||||
final fileOption =
|
final fileOption =
|
||||||
parent.target?.getByName('option', 'enable-file-transfer');
|
await bind.mainGetOption(key: 'enable-file-transfer');
|
||||||
_fileOk = fileOption?.isEmpty ?? false;
|
_fileOk = fileOption.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// input (mouse control)
|
// input (mouse control) false by default
|
||||||
Map<String, String> res = Map()
|
bind.mainSetOption(key: "enable-keyboard", value: "N");
|
||||||
..["name"] = "enable-keyboard"
|
|
||||||
..["value"] = 'N';
|
|
||||||
parent.target
|
|
||||||
?.setByName('option', jsonEncode(res)); // input false by default
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
Timer.periodic(Duration(seconds: 1), (timer) {
|
Timer.periodic(Duration(seconds: 1), (timer) async {
|
||||||
var status =
|
var status = await bind.mainGetOnlineStatue();
|
||||||
int.tryParse(parent.target?.getByName('connect_statue') ?? "") ?? 0;
|
|
||||||
if (status > 0) {
|
if (status > 0) {
|
||||||
status = 1;
|
status = 1;
|
||||||
}
|
}
|
||||||
@ -141,10 +129,8 @@ class ServerModel with ChangeNotifier {
|
|||||||
_connectStatus = status;
|
_connectStatus = status;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
final res = parent.target
|
final res = await bind.mainCheckClientsLength(length: _clients.length);
|
||||||
?.getByName('check_clients_length', _clients.length.toString()) ??
|
if (res != null) {
|
||||||
"";
|
|
||||||
if (res.isNotEmpty) {
|
|
||||||
debugPrint("clients not match!");
|
debugPrint("clients not match!");
|
||||||
updateClientState(res);
|
updateClientState(res);
|
||||||
}
|
}
|
||||||
@ -153,11 +139,13 @@ class ServerModel with ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePasswordModel() {
|
updatePasswordModel() async {
|
||||||
var update = false;
|
var update = false;
|
||||||
final temporaryPassword = gFFI.getByName("temporary_password");
|
final temporaryPassword = await bind.mainGetTemporaryPassword();
|
||||||
final verificationMethod = gFFI.getOption("verification-method");
|
final verificationMethod =
|
||||||
final temporaryPasswordLength = gFFI.getOption("temporary-password-length");
|
await bind.mainGetOption(key: "verification-method");
|
||||||
|
final temporaryPasswordLength =
|
||||||
|
await bind.mainGetOption(key: "temporary-password-length");
|
||||||
final oldPwdText = _serverPasswd.text;
|
final oldPwdText = _serverPasswd.text;
|
||||||
if (_serverPasswd.text != temporaryPassword) {
|
if (_serverPasswd.text != temporaryPassword) {
|
||||||
_serverPasswd.text = temporaryPassword;
|
_serverPasswd.text = temporaryPassword;
|
||||||
@ -191,10 +179,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_audioOk = !_audioOk;
|
_audioOk = !_audioOk;
|
||||||
Map<String, String> res = Map()
|
bind.mainSetOption(key: "enable-audio", value: _audioOk ? '' : 'N');
|
||||||
..["name"] = "enable-audio"
|
|
||||||
..["value"] = _audioOk ? '' : 'N';
|
|
||||||
parent.target?.setByName('option', jsonEncode(res));
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,10 +193,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fileOk = !_fileOk;
|
_fileOk = !_fileOk;
|
||||||
Map<String, String> res = Map()
|
bind.mainSetOption(key: "enable-file-transfer", value: _fileOk ? '' : 'N');
|
||||||
..["name"] = "enable-file-transfer"
|
|
||||||
..["value"] = _fileOk ? '' : 'N';
|
|
||||||
parent.target?.setByName('option', jsonEncode(res));
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +263,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
// TODO
|
// TODO
|
||||||
parent.target?.ffiModel.updateEventListener("");
|
parent.target?.ffiModel.updateEventListener("");
|
||||||
await parent.target?.invokeMethod("init_service");
|
await parent.target?.invokeMethod("init_service");
|
||||||
parent.target?.setByName("start_service");
|
await bind.mainStartService();
|
||||||
_fetchID();
|
_fetchID();
|
||||||
updateClientState();
|
updateClientState();
|
||||||
if (!Platform.isLinux) {
|
if (!Platform.isLinux) {
|
||||||
@ -296,7 +278,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
// TODO
|
// TODO
|
||||||
parent.target?.serverModel.closeAll();
|
parent.target?.serverModel.closeAll();
|
||||||
await parent.target?.invokeMethod("stop_service");
|
await parent.target?.invokeMethod("stop_service");
|
||||||
parent.target?.setByName("stop_service");
|
await bind.mainStopService();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (!Platform.isLinux) {
|
if (!Platform.isLinux) {
|
||||||
// current linux is not supported
|
// current linux is not supported
|
||||||
@ -309,9 +291,9 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> setPermanentPassword(String newPW) async {
|
Future<bool> setPermanentPassword(String newPW) async {
|
||||||
parent.target?.setByName("permanent_password", newPW);
|
await bind.mainSetPermanentPassword(password: newPW);
|
||||||
await Future.delayed(Duration(milliseconds: 500));
|
await Future.delayed(Duration(milliseconds: 500));
|
||||||
final pw = parent.target?.getByName("permanent_password");
|
final pw = await bind.mainGetPermanentPassword();
|
||||||
if (newPW == pw) {
|
if (newPW == pw) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -325,7 +307,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
const maxCount = 10;
|
const maxCount = 10;
|
||||||
while (count < maxCount) {
|
while (count < maxCount) {
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
final id = parent.target?.getByName("server_id") ?? "";
|
final id = await bind.mainGetMyId();
|
||||||
if (id.isEmpty) {
|
if (id.isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@ -352,10 +334,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
break;
|
break;
|
||||||
case "input":
|
case "input":
|
||||||
if (_inputOk != value) {
|
if (_inputOk != value) {
|
||||||
Map<String, String> res = Map()
|
bind.mainSetOption(key: "enable-keyboard", value: value ? '' : 'N');
|
||||||
..["name"] = "enable-keyboard"
|
|
||||||
..["value"] = value ? '' : 'N';
|
|
||||||
parent.target?.setByName('option', jsonEncode(res));
|
|
||||||
}
|
}
|
||||||
_inputOk = value;
|
_inputOk = value;
|
||||||
break;
|
break;
|
||||||
@ -365,8 +344,8 @@ class ServerModel with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateClientState([String? json]) {
|
updateClientState([String? json]) async {
|
||||||
var res = json ?? parent.target?.getByName("clients_state") ?? "";
|
var res = await bind.mainGetClientsState();
|
||||||
try {
|
try {
|
||||||
final List clientsJson = jsonDecode(res);
|
final List clientsJson = jsonDecode(res);
|
||||||
for (var clientJson in clientsJson) {
|
for (var clientJson in clientsJson) {
|
||||||
@ -448,12 +427,9 @@ class ServerModel with ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendLoginResponse(Client client, bool res) {
|
void sendLoginResponse(Client client, bool res) async {
|
||||||
final Map<String, dynamic> response = Map();
|
|
||||||
response["id"] = client.id;
|
|
||||||
response["res"] = res;
|
|
||||||
if (res) {
|
if (res) {
|
||||||
parent.target?.setByName("login_res", jsonEncode(response));
|
bind.serverLoginRes(connId: client.id, res: res);
|
||||||
if (!client.isFileTransfer) {
|
if (!client.isFileTransfer) {
|
||||||
parent.target?.invokeMethod("start_capture");
|
parent.target?.invokeMethod("start_capture");
|
||||||
}
|
}
|
||||||
@ -461,7 +437,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
_clients[client.id]?.authorized = true;
|
_clients[client.id]?.authorized = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
} else {
|
} else {
|
||||||
parent.target?.setByName("login_res", jsonEncode(response));
|
bind.serverLoginRes(connId: client.id, res: res);
|
||||||
parent.target?.invokeMethod("cancel_notification", client.id);
|
parent.target?.invokeMethod("cancel_notification", client.id);
|
||||||
_clients.remove(client.id);
|
_clients.remove(client.id);
|
||||||
}
|
}
|
||||||
@ -493,7 +469,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
closeAll() {
|
closeAll() {
|
||||||
_clients.forEach((id, client) {
|
_clients.forEach((id, client) {
|
||||||
parent.target?.setByName("close_conn", id.toString());
|
bind.serverCloseConnection(connId: id);
|
||||||
});
|
});
|
||||||
_clients.clear();
|
_clients.clear();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
|
|
||||||
|
import '../common.dart';
|
||||||
|
|
||||||
Future<void> initTray({List<MenuItem>? extra_item}) async {
|
Future<void> initTray({List<MenuItem>? extra_item}) async {
|
||||||
List<MenuItem> items = [
|
List<MenuItem> items = [
|
||||||
MenuItem(key: "show", label: translate("show rustdesk")),
|
MenuItem(key: "show", label: translate("show rustdesk")),
|
||||||
|
@ -1280,7 +1280,7 @@ impl LoginConfigHandler {
|
|||||||
/// Create a [`Message`] for login.
|
/// Create a [`Message`] for login.
|
||||||
fn create_login_msg(&self, password: Vec<u8>) -> Message {
|
fn create_login_msg(&self, password: Vec<u8>) -> Message {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
let my_id = Config::get_id_or(crate::common::FLUTTER_INFO1.lock().unwrap().clone());
|
let my_id = Config::get_id_or(crate::common::DEVICE_ID.lock().unwrap().clone());
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
let my_id = Config::get_id();
|
let my_id = Config::get_id();
|
||||||
let mut lr = LoginRequest {
|
let mut lr = LoginRequest {
|
||||||
|
@ -28,8 +28,8 @@ lazy_static::lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref FLUTTER_INFO1: Arc<Mutex<String>> = Default::default();
|
pub static ref DEVICE_ID: Arc<Mutex<String>> = Default::default();
|
||||||
pub static ref FLUTTER_INFO2: Arc<Mutex<String>> = Default::default();
|
pub static ref DEVICE_NAME: Arc<Mutex<String>> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -441,7 +441,7 @@ pub fn username() -> String {
|
|||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
return whoami::username().trim_end_matches('\0').to_owned();
|
return whoami::username().trim_end_matches('\0').to_owned();
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
return FLUTTER_INFO2.lock().unwrap().clone();
|
return DEVICE_NAME.lock().unwrap().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -23,10 +23,11 @@ use crate::ui_interface;
|
|||||||
use crate::ui_interface::{change_id, check_connect_status, is_ok_change_id};
|
use crate::ui_interface::{change_id, check_connect_status, is_ok_change_id};
|
||||||
use crate::ui_interface::{
|
use crate::ui_interface::{
|
||||||
discover, forget_password, get_api_server, get_app_name, get_async_job_status,
|
discover, forget_password, get_api_server, get_app_name, get_async_job_status,
|
||||||
get_connect_status, get_fav, get_id, get_lan_peers, get_license, get_local_option, get_options,
|
get_connect_status, get_fav, get_id, get_lan_peers, get_license, get_local_option, get_option,
|
||||||
get_peer, get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version,
|
get_options, get_peer, get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version,
|
||||||
has_rendezvous_service, post_request, set_local_option, set_options, set_peer_option,
|
has_rendezvous_service, post_request, set_local_option, set_option, set_options,
|
||||||
set_socks, store_fav, test_if_valid_server, using_public_server,
|
set_peer_option, set_permanent_password, set_socks, store_fav, test_if_valid_server,
|
||||||
|
update_temporary_password, using_public_server,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn initialize(app_dir: &str) {
|
fn initialize(app_dir: &str) {
|
||||||
@ -81,14 +82,24 @@ pub enum EventToUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_global_event_stream(s: StreamSink<String>, app_type: String) -> ResultType<()> {
|
pub fn start_global_event_stream(s: StreamSink<String>, app_type: String) -> ResultType<()> {
|
||||||
if let Some(_) = flutter::GLOBAL_EVENT_STREAM.write().unwrap().insert(app_type.clone(), s) {
|
if let Some(_) = flutter::GLOBAL_EVENT_STREAM
|
||||||
log::warn!("Global event stream of type {} is started before, but now removed", app_type);
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(app_type.clone(), s)
|
||||||
|
{
|
||||||
|
log::warn!(
|
||||||
|
"Global event stream of type {} is started before, but now removed",
|
||||||
|
app_type
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_global_event_stream(app_type: String) {
|
pub fn stop_global_event_stream(app_type: String) {
|
||||||
let _ = flutter::GLOBAL_EVENT_STREAM.write().unwrap().remove(&app_type);
|
let _ = flutter::GLOBAL_EVENT_STREAM
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.remove(&app_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn host_stop_system_key_propagate(stopped: bool) {
|
pub fn host_stop_system_key_propagate(stopped: bool) {
|
||||||
@ -113,7 +124,6 @@ pub fn get_session_remember(id: String) -> Option<bool> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO sync
|
|
||||||
pub fn get_session_toggle_option(id: String, arg: String) -> Option<bool> {
|
pub fn get_session_toggle_option(id: String, arg: String) -> Option<bool> {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
Some(session.get_toggle_option(&arg))
|
Some(session.get_toggle_option(&arg))
|
||||||
@ -143,7 +153,6 @@ pub fn get_session_option(id: String, arg: String) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// void
|
|
||||||
pub fn session_login(id: String, password: String, remember: bool) {
|
pub fn session_login(id: String, password: String, remember: bool) {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
session.login(&password, remember);
|
session.login(&password, remember);
|
||||||
@ -227,38 +236,6 @@ pub fn session_send_chat(id: String, text: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if let Some(_type) = m.get("type") {
|
|
||||||
// mask = match _type.as_str() {
|
|
||||||
// "down" => 1,
|
|
||||||
// "up" => 2,
|
|
||||||
// "wheel" => 3,
|
|
||||||
// _ => 0,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// if let Some(buttons) = m.get("buttons") {
|
|
||||||
// mask |= match buttons.as_str() {
|
|
||||||
// "left" => 1,
|
|
||||||
// "right" => 2,
|
|
||||||
// "wheel" => 4,
|
|
||||||
// _ => 0,
|
|
||||||
// } << 3;
|
|
||||||
// }
|
|
||||||
// TODO
|
|
||||||
pub fn session_send_mouse(
|
|
||||||
id: String,
|
|
||||||
mask: i32,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
alt: bool,
|
|
||||||
ctrl: bool,
|
|
||||||
shift: bool,
|
|
||||||
command: bool,
|
|
||||||
) {
|
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
|
||||||
session.send_mouse(mask, x, y, alt, ctrl, shift, command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn session_peer_option(id: String, name: String, value: String) {
|
pub fn session_peer_option(id: String, name: String, value: String) {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
session.set_option(name, value);
|
session.set_option(name, value);
|
||||||
@ -409,6 +386,22 @@ pub fn main_get_async_status() -> String {
|
|||||||
get_async_job_status()
|
get_async_job_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_get_option(key: String) -> String {
|
||||||
|
get_option(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_set_option(key: String, value: String) {
|
||||||
|
if key.eq("custom-rendezvous-server") {
|
||||||
|
set_option(key, value);
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
crate::rendezvous_mediator::RendezvousMediator::restart();
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
|
||||||
|
crate::common::test_rendezvous_server();
|
||||||
|
} else {
|
||||||
|
set_option(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_get_options() -> String {
|
pub fn main_get_options() -> String {
|
||||||
get_options()
|
get_options()
|
||||||
}
|
}
|
||||||
@ -452,7 +445,7 @@ pub fn main_store_fav(favs: Vec<String>) {
|
|||||||
store_fav(favs)
|
store_fav(favs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_get_peers(id: String) -> String {
|
pub fn main_get_peer(id: String) -> String {
|
||||||
let conf = get_peer(id);
|
let conf = get_peer(id);
|
||||||
serde_json::to_string(&conf).unwrap_or("".to_string())
|
serde_json::to_string(&conf).unwrap_or("".to_string())
|
||||||
}
|
}
|
||||||
@ -525,13 +518,30 @@ pub fn main_forget_password(id: String) {
|
|||||||
forget_password(id)
|
forget_password(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO APP_DIR & ui_interface
|
||||||
|
pub fn main_get_recent_peers() -> String {
|
||||||
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
|
let peers: Vec<(String, config::PeerInfoSerde)> = PeerConfig::peers()
|
||||||
|
.drain(..)
|
||||||
|
.map(|(id, _, p)| (id, p.info))
|
||||||
|
.collect();
|
||||||
|
serde_json::ser::to_string(&peers).unwrap_or("".to_owned())
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_load_recent_peers() {
|
pub fn main_load_recent_peers() {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
let peers: Vec<(String, config::PeerInfoSerde)> = PeerConfig::peers()
|
let peers: Vec<(String, config::PeerInfoSerde)> = PeerConfig::peers()
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|(id, _, p)| (id, p.info))
|
.map(|(id, _, p)| (id, p.info))
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(flutter::APP_TYPE_MAIN) {
|
if let Some(s) = flutter::GLOBAL_EVENT_STREAM
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(flutter::APP_TYPE_MAIN)
|
||||||
|
{
|
||||||
let data = HashMap::from([
|
let data = HashMap::from([
|
||||||
("name", "load_recent_peers".to_owned()),
|
("name", "load_recent_peers".to_owned()),
|
||||||
(
|
(
|
||||||
@ -557,7 +567,11 @@ pub fn main_load_fav_peers() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(flutter::APP_TYPE_MAIN) {
|
if let Some(s) = flutter::GLOBAL_EVENT_STREAM
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(flutter::APP_TYPE_MAIN)
|
||||||
|
{
|
||||||
let data = HashMap::from([
|
let data = HashMap::from([
|
||||||
("name", "load_fav_peers".to_owned()),
|
("name", "load_fav_peers".to_owned()),
|
||||||
(
|
(
|
||||||
@ -571,7 +585,11 @@ pub fn main_load_fav_peers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_load_lan_peers() {
|
pub fn main_load_lan_peers() {
|
||||||
if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(flutter::APP_TYPE_MAIN) {
|
if let Some(s) = flutter::GLOBAL_EVENT_STREAM
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(flutter::APP_TYPE_MAIN)
|
||||||
|
{
|
||||||
let data = HashMap::from([
|
let data = HashMap::from([
|
||||||
("name", "load_lan_peers".to_owned()),
|
("name", "load_lan_peers".to_owned()),
|
||||||
("peers", get_lan_peers()),
|
("peers", get_lan_peers()),
|
||||||
@ -580,532 +598,168 @@ pub fn main_load_lan_peers() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FFI for **get** commands which are idempotent.
|
pub fn main_get_last_remote_id() -> String {
|
||||||
/// Return result in c string.
|
// if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
///
|
// res = LocalConfig::get_remote_id();
|
||||||
/// # Arguments
|
// }
|
||||||
///
|
LocalConfig::get_remote_id()
|
||||||
/// * `name` - name of the command
|
}
|
||||||
/// * `arg` - argument of the command
|
|
||||||
#[no_mangle]
|
pub fn main_get_software_update_url() -> String {
|
||||||
unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *const c_char {
|
crate::common::SOFTWARE_UPDATE_URL.lock().unwrap().clone()
|
||||||
let mut res = "".to_owned();
|
}
|
||||||
let arg: &CStr = CStr::from_ptr(arg);
|
|
||||||
let name: &CStr = CStr::from_ptr(name);
|
pub fn main_get_home_dir() -> String {
|
||||||
if let Ok(name) = name.to_str() {
|
fs::get_home_as_string()
|
||||||
match name {
|
}
|
||||||
"peers" => {
|
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
pub fn main_get_langs() -> String {
|
||||||
let peers: Vec<(String, config::PeerInfoSerde)> = PeerConfig::peers()
|
crate::lang::LANGS.to_string()
|
||||||
.drain(..)
|
}
|
||||||
.map(|(id, _, p)| (id, p.info))
|
|
||||||
.collect();
|
pub fn main_get_temporary_password() -> String {
|
||||||
res = serde_json::ser::to_string(&peers).unwrap_or("".to_owned());
|
ui_interface::temporary_password()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"remote_id" => {
|
pub fn main_get_permanent_password() -> String {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
ui_interface::permanent_password()
|
||||||
res = LocalConfig::get_remote_id();
|
}
|
||||||
}
|
|
||||||
}
|
pub fn main_get_online_statue() -> i64 {
|
||||||
// "remember" => {
|
ONLINE.lock().unwrap().values().max().unwrap_or(&0).clone()
|
||||||
// res = Session::get_remember().to_string();
|
}
|
||||||
// }
|
|
||||||
// "toggle_option" => {
|
pub fn main_get_clients_state() -> String {
|
||||||
// if let Ok(arg) = arg.to_str() {
|
get_clients_state()
|
||||||
// if let Some(v) = Session::get_toggle_option(arg) {
|
}
|
||||||
// res = v.to_string();
|
|
||||||
// }
|
pub fn main_check_clients_length(length: usize) -> Option<String> {
|
||||||
// }
|
if length != get_clients_length() {
|
||||||
// }
|
Some(get_clients_state())
|
||||||
"test_if_valid_server" => {
|
} else {
|
||||||
if let Ok(arg) = arg.to_str() {
|
None
|
||||||
res = hbb_common::socket_client::test_if_valid_server(arg);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"option" => {
|
pub fn main_init(app_dir: String) {
|
||||||
if let Ok(arg) = arg.to_str() {
|
initialize(&app_dir);
|
||||||
res = ui_interface::get_option(arg.to_owned());
|
}
|
||||||
}
|
|
||||||
}
|
pub fn main_device_id(id: String) {
|
||||||
// "image_quality" => {
|
*crate::common::DEVICE_ID.lock().unwrap() = id;
|
||||||
// res = Session::get_image_quality();
|
}
|
||||||
// }
|
|
||||||
"software_update_url" => {
|
pub fn main_device_name(name: String) {
|
||||||
res = crate::common::SOFTWARE_UPDATE_URL.lock().unwrap().clone()
|
*crate::common::DEVICE_NAME.lock().unwrap() = name;
|
||||||
}
|
}
|
||||||
"translate" => {
|
|
||||||
if let Ok(arg) = arg.to_str() {
|
pub fn main_remove_peer(id: String) {
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(arg) {
|
PeerConfig::remove(&id);
|
||||||
if let Some(locale) = m.get("locale") {
|
}
|
||||||
if let Some(text) = m.get("text") {
|
|
||||||
res = crate::client::translate_locale(text.to_owned(), locale);
|
// TODO
|
||||||
}
|
pub fn session_send_mouse(id: String, msg: String) {
|
||||||
}
|
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(&msg) {
|
||||||
}
|
let alt = m.get("alt").is_some();
|
||||||
}
|
let ctrl = m.get("ctrl").is_some();
|
||||||
}
|
let shift = m.get("shift").is_some();
|
||||||
// "peer_option" => {
|
let command = m.get("command").is_some();
|
||||||
// if let Ok(arg) = arg.to_str() {
|
let x = m
|
||||||
// res = Session::get_option(arg);
|
.get("x")
|
||||||
// }
|
.map(|x| x.parse::<i32>().unwrap_or(0))
|
||||||
// }
|
.unwrap_or(0);
|
||||||
// File Action
|
let y = m
|
||||||
"get_home_dir" => {
|
.get("y")
|
||||||
res = fs::get_home_as_string();
|
.map(|x| x.parse::<i32>().unwrap_or(0))
|
||||||
}
|
.unwrap_or(0);
|
||||||
// "read_local_dir_sync" => {
|
let mut mask = 0;
|
||||||
// if let Ok(value) = arg.to_str() {
|
if let Some(_type) = m.get("type") {
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
mask = match _type.as_str() {
|
||||||
// if let (Some(path), Some(show_hidden)) =
|
"down" => 1,
|
||||||
// (m.get("path"), m.get("show_hidden"))
|
"up" => 2,
|
||||||
// {
|
"wheel" => 3,
|
||||||
// if let Ok(fd) =
|
_ => 0,
|
||||||
// fs::read_dir(&fs::get_path(path), show_hidden.eq("true"))
|
};
|
||||||
// {
|
}
|
||||||
// res = make_fd_to_json(fd);
|
if let Some(buttons) = m.get("buttons") {
|
||||||
// }
|
mask |= match buttons.as_str() {
|
||||||
// }
|
"left" => 1,
|
||||||
// }
|
"right" => 2,
|
||||||
// }
|
"wheel" => 4,
|
||||||
// }
|
_ => 0,
|
||||||
// Server Side
|
} << 3;
|
||||||
"local_option" => {
|
}
|
||||||
if let Ok(arg) = arg.to_str() {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
res = LocalConfig::get_option(arg);
|
session.send_mouse(mask, x, y, alt, ctrl, shift, command);
|
||||||
}
|
|
||||||
}
|
|
||||||
"langs" => {
|
|
||||||
res = crate::lang::LANGS.to_string();
|
|
||||||
}
|
|
||||||
"server_id" => {
|
|
||||||
res = ui_interface::get_id();
|
|
||||||
}
|
|
||||||
"temporary_password" => {
|
|
||||||
res = ui_interface::temporary_password();
|
|
||||||
}
|
|
||||||
"permanent_password" => {
|
|
||||||
res = ui_interface::permanent_password();
|
|
||||||
}
|
|
||||||
"connect_statue" => {
|
|
||||||
res = ONLINE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.values()
|
|
||||||
.max()
|
|
||||||
.unwrap_or(&0)
|
|
||||||
.clone()
|
|
||||||
.to_string();
|
|
||||||
}
|
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
|
||||||
"clients_state" => {
|
|
||||||
res = get_clients_state();
|
|
||||||
}
|
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
|
||||||
"check_clients_length" => {
|
|
||||||
if let Ok(value) = arg.to_str() {
|
|
||||||
if value.parse::<usize>().unwrap_or(usize::MAX) != get_clients_length() {
|
|
||||||
res = get_clients_state()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"uuid" => {
|
|
||||||
res = base64::encode(get_uuid());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::error!("Unknown name of get_by_name: {}", name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_restart_remote_device(id: String) {
|
||||||
|
// TODO
|
||||||
|
// Session::restart_remote_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_set_home_dir(home: String) {
|
||||||
|
*config::APP_HOME_DIR.write().unwrap() = home;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_stop_service() {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
Config::set_option("stop-service".into(), "Y".into());
|
||||||
|
crate::rendezvous_mediator::RendezvousMediator::restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_start_service() {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
Config::set_option("stop-service".into(), "".into());
|
||||||
|
crate::rendezvous_mediator::RendezvousMediator::restart();
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
std::thread::spawn(move || start_server(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_update_temporary_password() {
|
||||||
|
update_temporary_password();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_set_permanent_password(password: String) {
|
||||||
|
set_permanent_password(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server_send_chat(conn_id: i32, msg: String) {
|
||||||
|
connection_manager::send_chat(conn_id, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server_login_res(conn_id: i32, res: bool) {
|
||||||
|
connection_manager::on_login_res(conn_id, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server_close_connection(conn_id: i32) {
|
||||||
|
connection_manager::close_conn(conn_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn translate(name: *const c_char, locale: *const c_char) -> *const c_char {
|
||||||
|
let name = CStr::from_ptr(name);
|
||||||
|
let locale = CStr::from_ptr(locale);
|
||||||
|
let res = if let (Ok(name), Ok(locale)) = (name.to_str(), locale.to_str()) {
|
||||||
|
crate::client::translate_locale(name.to_owned(), locale)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
CString::from_vec_unchecked(res.into_bytes()).into_raw()
|
CString::from_vec_unchecked(res.into_bytes()).into_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FFI for **set** commands which are not idempotent.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `name` - name of the command
|
|
||||||
/// * `arg` - argument of the command
|
|
||||||
#[no_mangle]
|
|
||||||
unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
|
|
||||||
let value: &CStr = CStr::from_ptr(value);
|
|
||||||
if let Ok(value) = value.to_str() {
|
|
||||||
let name: &CStr = CStr::from_ptr(name);
|
|
||||||
if let Ok(name) = name.to_str() {
|
|
||||||
match name {
|
|
||||||
"init" => {
|
|
||||||
initialize(value);
|
|
||||||
}
|
|
||||||
"info1" => {
|
|
||||||
*crate::common::FLUTTER_INFO1.lock().unwrap() = value.to_owned();
|
|
||||||
}
|
|
||||||
"info2" => {
|
|
||||||
*crate::common::FLUTTER_INFO2.lock().unwrap() = value.to_owned();
|
|
||||||
}
|
|
||||||
// "connect" => {
|
|
||||||
// Session::start(value, false);
|
|
||||||
// }
|
|
||||||
// "connect_file_transfer" => {
|
|
||||||
// Session::start(value, true);
|
|
||||||
// }
|
|
||||||
// "login" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let Some(password) = m.get("password") {
|
|
||||||
// if let Some(remember) = m.get("remember") {
|
|
||||||
// Session::login(password, remember == "true");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "close" => {
|
|
||||||
// Session::close();
|
|
||||||
// }
|
|
||||||
// "refresh" => {
|
|
||||||
// Session::refresh();
|
|
||||||
// }
|
|
||||||
// "reconnect" => {
|
|
||||||
// Session::reconnect();
|
|
||||||
// }
|
|
||||||
// "toggle_option" => {
|
|
||||||
// Session::toggle_option(value);
|
|
||||||
// }
|
|
||||||
// "image_quality" => {
|
|
||||||
// Session::set_image_quality(value);
|
|
||||||
// }
|
|
||||||
// "lock_screen" => {
|
|
||||||
// Session::lock_screen();
|
|
||||||
// }
|
|
||||||
// "ctrl_alt_del" => {
|
|
||||||
// Session::ctrl_alt_del();
|
|
||||||
// }
|
|
||||||
// "switch_display" => {
|
|
||||||
// if let Ok(v) = value.parse::<i32>() {
|
|
||||||
// Session::switch_display(v);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
"remove" => {
|
|
||||||
PeerConfig::remove(value);
|
|
||||||
}
|
|
||||||
// "input_key" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// let alt = m.get("alt").is_some();
|
|
||||||
// let ctrl = m.get("ctrl").is_some();
|
|
||||||
// let shift = m.get("shift").is_some();
|
|
||||||
// let command = m.get("command").is_some();
|
|
||||||
// let down = m.get("down").is_some();
|
|
||||||
// let press = m.get("press").is_some();
|
|
||||||
// if let Some(name) = m.get("name") {
|
|
||||||
// Session::input_key(name, down, press, alt, ctrl, shift, command);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "input_string" => {
|
|
||||||
// Session::input_string(value);
|
|
||||||
// }
|
|
||||||
// "chat_client_mode" => {
|
|
||||||
// Session::send_chat(value.to_owned());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
"send_mouse" => {
|
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
let id = m.get("id");
|
|
||||||
if id.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let id = id.unwrap();
|
|
||||||
let alt = m.get("alt").is_some();
|
|
||||||
let ctrl = m.get("ctrl").is_some();
|
|
||||||
let shift = m.get("shift").is_some();
|
|
||||||
let command = m.get("command").is_some();
|
|
||||||
let x = m
|
|
||||||
.get("x")
|
|
||||||
.map(|x| x.parse::<i32>().unwrap_or(0))
|
|
||||||
.unwrap_or(0);
|
|
||||||
let y = m
|
|
||||||
.get("y")
|
|
||||||
.map(|x| x.parse::<i32>().unwrap_or(0))
|
|
||||||
.unwrap_or(0);
|
|
||||||
let mut mask = 0;
|
|
||||||
if let Some(_type) = m.get("type") {
|
|
||||||
mask = match _type.as_str() {
|
|
||||||
"down" => 1,
|
|
||||||
"up" => 2,
|
|
||||||
"wheel" => 3,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if let Some(buttons) = m.get("buttons") {
|
|
||||||
mask |= match buttons.as_str() {
|
|
||||||
"left" => 1,
|
|
||||||
"right" => 2,
|
|
||||||
"wheel" => 4,
|
|
||||||
_ => 0,
|
|
||||||
} << 3;
|
|
||||||
}
|
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(id) {
|
|
||||||
session.send_mouse(mask, x, y, alt, ctrl, shift, command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"option" => {
|
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
if let Some(name) = m.get("name") {
|
|
||||||
if let Some(value) = m.get("value") {
|
|
||||||
ui_interface::set_option(name.to_owned(), value.to_owned());
|
|
||||||
if name == "custom-rendezvous-server" {
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
crate::rendezvous_mediator::RendezvousMediator::restart();
|
|
||||||
#[cfg(any(
|
|
||||||
target_os = "android",
|
|
||||||
target_os = "ios",
|
|
||||||
feature = "cli"
|
|
||||||
))]
|
|
||||||
crate::common::test_rendezvous_server();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// "peer_option" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let Some(name) = m.get("name") {
|
|
||||||
// if let Some(value) = m.get("value") {
|
|
||||||
// Session::set_option(name.to_owned(), value.to_owned());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
"local_option" => {
|
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
if let Some(name) = m.get("name") {
|
|
||||||
if let Some(value) = m.get("value") {
|
|
||||||
LocalConfig::set_option(name.to_owned(), value.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// "input_os_password" => {
|
|
||||||
// Session::input_os_password(value.to_owned(), true);
|
|
||||||
// }
|
|
||||||
"restart_remote_device" => {
|
|
||||||
// TODO
|
|
||||||
// Session::restart_remote_device();
|
|
||||||
}
|
|
||||||
// // File Action
|
|
||||||
// "read_remote_dir" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (Some(path), Some(show_hidden), Some(session)) = (
|
|
||||||
// m.get("path"),
|
|
||||||
// m.get("show_hidden"),
|
|
||||||
// Session::get().read().unwrap().as_ref(),
|
|
||||||
// ) {
|
|
||||||
// session.read_remote_dir(path.to_owned(), show_hidden.eq("true"));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "send_files" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (
|
|
||||||
// Some(id),
|
|
||||||
// Some(path),
|
|
||||||
// Some(to),
|
|
||||||
// Some(file_num),
|
|
||||||
// Some(show_hidden),
|
|
||||||
// Some(is_remote),
|
|
||||||
// ) = (
|
|
||||||
// m.get("id"),
|
|
||||||
// m.get("path"),
|
|
||||||
// m.get("to"),
|
|
||||||
// m.get("file_num"),
|
|
||||||
// m.get("show_hidden"),
|
|
||||||
// m.get("is_remote"),
|
|
||||||
// ) {
|
|
||||||
// Session::send_files(
|
|
||||||
// id.parse().unwrap_or(0),
|
|
||||||
// path.to_owned(),
|
|
||||||
// to.to_owned(),
|
|
||||||
// file_num.parse().unwrap_or(0),
|
|
||||||
// show_hidden.eq("true"),
|
|
||||||
// is_remote.eq("true"),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "set_confirm_override_file" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (
|
|
||||||
// Some(id),
|
|
||||||
// Some(file_num),
|
|
||||||
// Some(need_override),
|
|
||||||
// Some(remember),
|
|
||||||
// Some(is_upload),
|
|
||||||
// ) = (
|
|
||||||
// m.get("id"),
|
|
||||||
// m.get("file_num"),
|
|
||||||
// m.get("need_override"),
|
|
||||||
// m.get("remember"),
|
|
||||||
// m.get("is_upload"),
|
|
||||||
// ) {
|
|
||||||
// Session::set_confirm_override_file(
|
|
||||||
// id.parse().unwrap_or(0),
|
|
||||||
// file_num.parse().unwrap_or(0),
|
|
||||||
// need_override.eq("true"),
|
|
||||||
// remember.eq("true"),
|
|
||||||
// is_upload.eq("true"),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ** TODO ** continue
|
|
||||||
// "remove_file" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (
|
|
||||||
// Some(id),
|
|
||||||
// Some(path),
|
|
||||||
// Some(file_num),
|
|
||||||
// Some(is_remote),
|
|
||||||
// Some(session),
|
|
||||||
// ) = (
|
|
||||||
// m.get("id"),
|
|
||||||
// m.get("path"),
|
|
||||||
// m.get("file_num"),
|
|
||||||
// m.get("is_remote"),
|
|
||||||
// Session::get().write().unwrap().as_mut(),
|
|
||||||
// ) {
|
|
||||||
// session.remove_file(
|
|
||||||
// id.parse().unwrap_or(0),
|
|
||||||
// path.to_owned(),
|
|
||||||
// file_num.parse().unwrap_or(0),
|
|
||||||
// is_remote.eq("true"),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "read_dir_recursive" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (Some(id), Some(path), Some(is_remote), Some(session)) = (
|
|
||||||
// m.get("id"),
|
|
||||||
// m.get("path"),
|
|
||||||
// m.get("is_remote"),
|
|
||||||
// Session::get().write().unwrap().as_mut(),
|
|
||||||
// ) {
|
|
||||||
// session.remove_dir_all(
|
|
||||||
// id.parse().unwrap_or(0),
|
|
||||||
// path.to_owned(),
|
|
||||||
// is_remote.eq("true"),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "remove_all_empty_dirs" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (Some(id), Some(path), Some(is_remote), Some(session)) = (
|
|
||||||
// m.get("id"),
|
|
||||||
// m.get("path"),
|
|
||||||
// m.get("is_remote"),
|
|
||||||
// Session::get().write().unwrap().as_mut(),
|
|
||||||
// ) {
|
|
||||||
// session.remove_dir(
|
|
||||||
// id.parse().unwrap_or(0),
|
|
||||||
// path.to_owned(),
|
|
||||||
// is_remote.eq("true"),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "cancel_job" => {
|
|
||||||
// if let (Ok(id), Some(session)) =
|
|
||||||
// (value.parse(), Session::get().write().unwrap().as_mut())
|
|
||||||
// {
|
|
||||||
// session.cancel_job(id);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "create_dir" => {
|
|
||||||
// if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
|
||||||
// if let (Some(id), Some(path), Some(is_remote), Some(session)) = (
|
|
||||||
// m.get("id"),
|
|
||||||
// m.get("path"),
|
|
||||||
// m.get("is_remote"),
|
|
||||||
// Session::get().write().unwrap().as_mut(),
|
|
||||||
// ) {
|
|
||||||
// session.create_dir(
|
|
||||||
// id.parse().unwrap_or(0),
|
|
||||||
// path.to_owned(),
|
|
||||||
// is_remote.eq("true"),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Server Side
|
|
||||||
// "update_password" => {
|
|
||||||
// if value.is_empty() {
|
|
||||||
// Config::set_password(&Config::get_auto_password());
|
|
||||||
// } else {
|
|
||||||
// Config::set_password(value);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
"chat_server_mode" => {
|
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, Value>>(value) {
|
|
||||||
if let (Some(Value::Number(id)), Some(Value::String(text))) =
|
|
||||||
(m.get("id"), m.get("text"))
|
|
||||||
{
|
|
||||||
let id = id.as_i64().unwrap_or(0);
|
|
||||||
connection_manager::send_chat(id as i32, text.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"home_dir" => {
|
|
||||||
*config::APP_HOME_DIR.write().unwrap() = value.to_owned();
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
"login_res" => {
|
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, Value>>(value) {
|
|
||||||
if let (Some(Value::Number(id)), Some(Value::Bool(res))) =
|
|
||||||
(m.get("id"), m.get("res"))
|
|
||||||
{
|
|
||||||
let id = id.as_i64().unwrap_or(0);
|
|
||||||
connection_manager::on_login_res(id as i32, *res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
"stop_service" => {
|
|
||||||
Config::set_option("stop-service".into(), "Y".into());
|
|
||||||
crate::rendezvous_mediator::RendezvousMediator::restart();
|
|
||||||
}
|
|
||||||
"start_service" => {
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
{
|
|
||||||
Config::set_option("stop-service".into(), "".into());
|
|
||||||
crate::rendezvous_mediator::RendezvousMediator::restart();
|
|
||||||
}
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
std::thread::spawn(move || start_server(true));
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
"close_conn" => {
|
|
||||||
if let Ok(id) = value.parse::<i32>() {
|
|
||||||
connection_manager::close_conn(id);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
"temporary_password" => {
|
|
||||||
ui_interface::update_temporary_password();
|
|
||||||
}
|
|
||||||
"permanent_password" => {
|
|
||||||
ui_interface::set_permanent_password(value.to_owned());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::error!("Unknown name of set_by_name: {}", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_query_onlines(onlines: Vec<String>, offlines: Vec<String>) {
|
fn handle_query_onlines(onlines: Vec<String>, offlines: Vec<String>) {
|
||||||
if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(flutter::APP_TYPE_MAIN) {
|
if let Some(s) = flutter::GLOBAL_EVENT_STREAM
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(flutter::APP_TYPE_MAIN)
|
||||||
|
{
|
||||||
let data = HashMap::from([
|
let data = HashMap::from([
|
||||||
("name", "callback_query_onlines".to_owned()),
|
("name", "callback_query_onlines".to_owned()),
|
||||||
("onlines", onlines.join(",")),
|
("onlines", onlines.join(",")),
|
||||||
|
@ -5,7 +5,7 @@ use crate::clipboard_file::*;
|
|||||||
use crate::common::update_clipboard;
|
use crate::common::update_clipboard;
|
||||||
use crate::video_service;
|
use crate::video_service;
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
use crate::{common::FLUTTER_INFO2, flutter::connection_manager::start_channel};
|
use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel};
|
||||||
use crate::{ipc, VERSION};
|
use crate::{ipc, VERSION};
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::Config,
|
config::Config,
|
||||||
@ -643,7 +643,7 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
{
|
{
|
||||||
pi.hostname = FLUTTER_INFO2.lock().unwrap().clone();
|
pi.hostname = DEVICE_NAME.lock().unwrap().clone();
|
||||||
pi.platform = "Android".into();
|
pi.platform = "Android".into();
|
||||||
}
|
}
|
||||||
#[cfg(feature = "hwcodec")]
|
#[cfg(feature = "hwcodec")]
|
||||||
|
@ -138,10 +138,11 @@ pub fn get_license() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_option(key: String) -> String {
|
pub fn get_option(key: String) -> String {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
get_option_(&key)
|
||||||
return Config::get_option(&key);
|
// #[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
// return Config::get_option(&key);
|
||||||
return get_option_(&key);
|
// #[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
// return get_option_(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_option_(key: &str) -> String {
|
fn get_option_(key: &str) -> String {
|
||||||
@ -250,33 +251,31 @@ pub fn get_sound_inputs() -> Vec<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_options(m: HashMap<String, String>) {
|
pub fn set_options(m: HashMap<String, String>) {
|
||||||
|
*OPTIONS.lock().unwrap() = m.clone();
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
{
|
ipc::set_options(m).ok();
|
||||||
*OPTIONS.lock().unwrap() = m.clone();
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
ipc::set_options(m).ok();
|
Config::set_options(m);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_option(key: String, value: String) {
|
pub fn set_option(key: String, value: String) {
|
||||||
|
let mut options = OPTIONS.lock().unwrap();
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
if &key == "stop-service" {
|
||||||
|
let is_stop = value == "Y";
|
||||||
|
if is_stop && crate::platform::macos::uninstall() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value.is_empty() {
|
||||||
|
options.remove(&key);
|
||||||
|
} else {
|
||||||
|
options.insert(key.clone(), value.clone());
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
ipc::set_options(options.clone()).ok();
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
Config::set_option(key, value);
|
Config::set_option(key, value);
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
{
|
|
||||||
let mut options = OPTIONS.lock().unwrap();
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
if &key == "stop-service" {
|
|
||||||
let is_stop = value == "Y";
|
|
||||||
if is_stop && crate::platform::macos::uninstall() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if value.is_empty() {
|
|
||||||
options.remove(&key);
|
|
||||||
} else {
|
|
||||||
options.insert(key.clone(), value.clone());
|
|
||||||
}
|
|
||||||
ipc::set_options(options.clone()).ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_path() -> String {
|
pub fn install_path() -> String {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user