flutter_desktop: fix global envet stream shading && refactor platform ffi
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
d3bc0ca073
commit
7a2de5d280
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -7,15 +8,16 @@ import 'package:get/instance_manager.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'models/model.dart';
|
import 'models/model.dart';
|
||||||
|
import 'models/platform_model.dart';
|
||||||
|
|
||||||
final globalKey = GlobalKey<NavigatorState>();
|
final globalKey = GlobalKey<NavigatorState>();
|
||||||
final navigationBarKey = GlobalKey();
|
final navigationBarKey = GlobalKey();
|
||||||
|
|
||||||
var isAndroid = false;
|
var isAndroid = Platform.isAndroid;
|
||||||
var isIOS = false;
|
var isIOS = Platform.isIOS;
|
||||||
var isWeb = false;
|
var isWeb = false;
|
||||||
var isWebDesktop = false;
|
var isWebDesktop = false;
|
||||||
var isDesktop = false;
|
var isDesktop = Platform.isWindows || Platform.isMacOS || Platform.isLinux;
|
||||||
var version = "";
|
var version = "";
|
||||||
int androidVersion = 0;
|
int androidVersion = 0;
|
||||||
|
|
||||||
@ -119,9 +121,9 @@ class DialogManager {
|
|||||||
|
|
||||||
static Future<T?> show<T>(DialogBuilder builder,
|
static Future<T?> show<T>(DialogBuilder builder,
|
||||||
{bool clickMaskDismiss = false,
|
{bool clickMaskDismiss = false,
|
||||||
bool backDismiss = false,
|
bool backDismiss = false,
|
||||||
String? tag,
|
String? tag,
|
||||||
bool useAnimation = true}) async {
|
bool useAnimation = true}) async {
|
||||||
final t;
|
final t;
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
t = tag;
|
t = tag;
|
||||||
@ -146,10 +148,11 @@ class DialogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CustomAlertDialog extends StatelessWidget {
|
class CustomAlertDialog extends StatelessWidget {
|
||||||
CustomAlertDialog({required this.title,
|
CustomAlertDialog(
|
||||||
required this.content,
|
{required this.title,
|
||||||
required this.actions,
|
required this.content,
|
||||||
this.contentPadding});
|
required this.actions,
|
||||||
|
this.contentPadding});
|
||||||
|
|
||||||
final Widget title;
|
final Widget title;
|
||||||
final Widget content;
|
final Widget content;
|
||||||
@ -162,7 +165,7 @@ class CustomAlertDialog extends StatelessWidget {
|
|||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: title,
|
title: title,
|
||||||
contentPadding:
|
contentPadding:
|
||||||
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10),
|
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10),
|
||||||
content: content,
|
content: content,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
@ -364,9 +367,8 @@ Future<void> initGlobalFFI() async {
|
|||||||
_globalFFI = FFI();
|
_globalFFI = FFI();
|
||||||
// after `put`, can also be globally found by Get.find<FFI>();
|
// after `put`, can also be globally found by Get.find<FFI>();
|
||||||
Get.put(_globalFFI, permanent: true);
|
Get.put(_globalFFI, permanent: true);
|
||||||
await _globalFFI.ffiModel.init();
|
|
||||||
// trigger connection status updater
|
// trigger connection status updater
|
||||||
await _globalFFI.bind.mainCheckConnectStatus();
|
await bind.mainCheckConnectStatus();
|
||||||
// global shared preference
|
// global shared preference
|
||||||
await Get.putAsync(() => SharedPreferences.getInstance());
|
await Get.putAsync(() => SharedPreferences.getInstance());
|
||||||
}
|
}
|
||||||
|
@ -1 +1,4 @@
|
|||||||
double kDesktopRemoteTabBarHeight = 48.0;
|
double kDesktopRemoteTabBarHeight = 48.0;
|
||||||
|
String kAppTypeMain = "main";
|
||||||
|
String kAppTypeDesktopRemote = "remote";
|
||||||
|
String kAppTypeDesktopFileTransfer = "file transfer";
|
||||||
|
@ -16,6 +16,7 @@ import '../../mobile/pages/home_page.dart';
|
|||||||
import '../../mobile/pages/scan_page.dart';
|
import '../../mobile/pages/scan_page.dart';
|
||||||
import '../../mobile/pages/settings_page.dart';
|
import '../../mobile/pages/settings_page.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
|
|
||||||
// enum RemoteType { recently, favorite, discovered, addressBook }
|
// enum RemoteType { recently, favorite, discovered, addressBook }
|
||||||
|
|
||||||
@ -428,10 +429,10 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
|
|
||||||
updateStatus() async {
|
updateStatus() async {
|
||||||
svcStopped.value = gFFI.getOption("stop-service") == "Y";
|
svcStopped.value = gFFI.getOption("stop-service") == "Y";
|
||||||
final status = jsonDecode(await gFFI.bind.mainGetConnectStatus())
|
final status =
|
||||||
as Map<String, dynamic>;
|
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
|
||||||
svcStatusCode.value = status["status_num"];
|
svcStatusCode.value = status["status_num"];
|
||||||
svcIsUsingPublicServer.value = await gFFI.bind.mainIsUsingPublicServer();
|
svcIsUsingPublicServer.value = await bind.mainIsUsingPublicServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLogin() {
|
handleLogin() {
|
||||||
@ -906,13 +907,13 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
|||||||
if (_tabController.indexIsChanging) {
|
if (_tabController.indexIsChanging) {
|
||||||
switch (_tabController.index) {
|
switch (_tabController.index) {
|
||||||
case 0:
|
case 0:
|
||||||
gFFI.bind.mainLoadRecentPeers();
|
bind.mainLoadRecentPeers();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
gFFI.bind.mainLoadFavPeers();
|
bind.mainLoadFavPeers();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
gFFI.bind.mainDiscover();
|
bind.mainDiscover();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
break;
|
break;
|
||||||
|
@ -8,6 +8,7 @@ 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/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';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -630,13 +631,13 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
setState(() {
|
setState(() {
|
||||||
msg = "";
|
msg = "";
|
||||||
isInProgress = true;
|
isInProgress = true;
|
||||||
gFFI.bind.mainChangeId(newId: newId);
|
bind.mainChangeId(newId: newId);
|
||||||
});
|
});
|
||||||
|
|
||||||
var status = await gFFI.bind.mainGetAsyncStatus();
|
var status = await bind.mainGetAsyncStatus();
|
||||||
while (status == " ") {
|
while (status == " ") {
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
status = await gFFI.bind.mainGetAsyncStatus();
|
status = await bind.mainGetAsyncStatus();
|
||||||
}
|
}
|
||||||
if (status.isEmpty) {
|
if (status.isEmpty) {
|
||||||
// ok
|
// ok
|
||||||
@ -655,8 +656,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void changeServer() async {
|
void changeServer() async {
|
||||||
Map<String, dynamic> oldOptions =
|
Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions());
|
||||||
jsonDecode(await gFFI.bind.mainGetOptions());
|
|
||||||
print("${oldOptions}");
|
print("${oldOptions}");
|
||||||
String idServer = oldOptions['custom-rendezvous-server'] ?? "";
|
String idServer = oldOptions['custom-rendezvous-server'] ?? "";
|
||||||
var idServerMsg = "";
|
var idServerMsg = "";
|
||||||
@ -814,7 +814,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
|
|
||||||
if (idServer.isNotEmpty) {
|
if (idServer.isNotEmpty) {
|
||||||
idServerMsg = translate(
|
idServerMsg = translate(
|
||||||
await gFFI.bind.mainTestIfValidServer(server: idServer));
|
await bind.mainTestIfValidServer(server: idServer));
|
||||||
if (idServerMsg.isEmpty) {
|
if (idServerMsg.isEmpty) {
|
||||||
oldOptions['custom-rendezvous-server'] = idServer;
|
oldOptions['custom-rendezvous-server'] = idServer;
|
||||||
} else {
|
} else {
|
||||||
@ -826,8 +826,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (relayServer.isNotEmpty) {
|
if (relayServer.isNotEmpty) {
|
||||||
relayServerMsg = translate(await gFFI.bind
|
relayServerMsg = translate(
|
||||||
.mainTestIfValidServer(server: relayServer));
|
await bind.mainTestIfValidServer(server: relayServer));
|
||||||
if (relayServerMsg.isEmpty) {
|
if (relayServerMsg.isEmpty) {
|
||||||
oldOptions['relay-server'] = relayServer;
|
oldOptions['relay-server'] = relayServer;
|
||||||
} else {
|
} else {
|
||||||
@ -853,7 +853,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
}
|
}
|
||||||
// ok
|
// ok
|
||||||
oldOptions['key'] = key;
|
oldOptions['key'] = key;
|
||||||
await gFFI.bind.mainSetOptions(json: jsonEncode(oldOptions));
|
await bind.mainSetOptions(json: jsonEncode(oldOptions));
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
child: Text(translate("OK"))),
|
child: Text(translate("OK"))),
|
||||||
@ -863,8 +863,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void changeWhiteList() async {
|
void changeWhiteList() async {
|
||||||
Map<String, dynamic> oldOptions =
|
Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions());
|
||||||
jsonDecode(await gFFI.bind.mainGetOptions());
|
|
||||||
var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(',');
|
var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(',');
|
||||||
var newWhiteListField = newWhiteList.join('\n');
|
var newWhiteListField = newWhiteList.join('\n');
|
||||||
var msg = "";
|
var msg = "";
|
||||||
@ -935,7 +934,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
newWhiteList = ips.join(',');
|
newWhiteList = ips.join(',');
|
||||||
}
|
}
|
||||||
oldOptions['whitelist'] = newWhiteList;
|
oldOptions['whitelist'] = newWhiteList;
|
||||||
await gFFI.bind.mainSetOptions(json: jsonEncode(oldOptions));
|
await bind.mainSetOptions(json: jsonEncode(oldOptions));
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
child: Text(translate("OK"))),
|
child: Text(translate("OK"))),
|
||||||
@ -945,7 +944,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void changeSocks5Proxy() async {
|
void changeSocks5Proxy() async {
|
||||||
var socks = await gFFI.bind.mainGetSocks();
|
var socks = await bind.mainGetSocks();
|
||||||
|
|
||||||
String proxy = "";
|
String proxy = "";
|
||||||
String proxyMsg = "";
|
String proxyMsg = "";
|
||||||
@ -1072,7 +1071,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
|
|
||||||
if (proxy.isNotEmpty) {
|
if (proxy.isNotEmpty) {
|
||||||
proxyMsg = translate(
|
proxyMsg = translate(
|
||||||
await gFFI.bind.mainTestIfValidServer(server: proxy));
|
await bind.mainTestIfValidServer(server: proxy));
|
||||||
if (proxyMsg.isEmpty) {
|
if (proxyMsg.isEmpty) {
|
||||||
// ignore
|
// ignore
|
||||||
} else {
|
} else {
|
||||||
@ -1080,7 +1079,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await gFFI.bind.mainSetSocks(
|
await bind.mainSetSocks(
|
||||||
proxy: proxy, username: username, password: password);
|
proxy: proxy, username: username, password: password);
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
@ -1091,9 +1090,9 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void about() async {
|
void about() async {
|
||||||
final appName = await gFFI.bind.mainGetAppName();
|
final appName = await bind.mainGetAppName();
|
||||||
final license = await gFFI.bind.mainGetLicense();
|
final license = await bind.mainGetLicense();
|
||||||
final version = await gFFI.bind.mainGetVersion();
|
final version = await bind.mainGetVersion();
|
||||||
final linkStyle = TextStyle(decoration: TextDecoration.underline);
|
final linkStyle = TextStyle(decoration: TextDecoration.underline);
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
|
@ -11,6 +11,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';
|
||||||
|
|
||||||
class FileManagerPage extends StatefulWidget {
|
class FileManagerPage extends StatefulWidget {
|
||||||
FileManagerPage({Key? key, required this.id}) : super(key: key);
|
FileManagerPage({Key? key, required this.id}) : super(key: key);
|
||||||
@ -37,7 +38,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Get.put(FFI.newFFI()..connect(widget.id, isFileTransfer: true),
|
Get.put(FFI()..connect(widget.id, isFileTransfer: true),
|
||||||
tag: 'ft_${widget.id}');
|
tag: 'ft_${widget.id}');
|
||||||
// _ffi.ffiModel.updateEventListener(widget.id);
|
// _ffi.ffiModel.updateEventListener(widget.id);
|
||||||
if (!Platform.isLinux) {
|
if (!Platform.isLinux) {
|
||||||
@ -464,13 +465,15 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
decoration: BoxDecoration(color: Colors.blue),
|
decoration: BoxDecoration(color: Colors.blue),
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: EdgeInsets.all(8.0),
|
||||||
child: FutureBuilder<String>(
|
child: FutureBuilder<String>(
|
||||||
future: _ffi.bind.sessionGetPlatform(
|
future: bind.sessionGetPlatform(
|
||||||
id: _ffi.id, isRemote: !isLocal),
|
id: _ffi.id, isRemote: !isLocal),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||||
return getPlatformImage('${snapshot.data}');
|
return getPlatformImage('${snapshot.data}');
|
||||||
} else {
|
} else {
|
||||||
return CircularProgressIndicator(color: Colors.white,);
|
return CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
Text(isLocal
|
Text(isLocal
|
||||||
@ -505,21 +508,25 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
border: Border.all(color: Colors.black12)),
|
border: Border.all(color: Colors.black12)),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
prefix: Padding(padding: EdgeInsets.only(left: 4.0)),
|
prefix:
|
||||||
suffix: DropdownButton<String>(
|
Padding(padding: EdgeInsets.only(left: 4.0)),
|
||||||
isDense: true,
|
suffix: DropdownButton<String>(
|
||||||
underline: Offstage(),
|
isDense: true,
|
||||||
items: [
|
underline: Offstage(),
|
||||||
// TODO: favourite
|
items: [
|
||||||
DropdownMenuItem(child: Text('/'), value: '/',)
|
// TODO: favourite
|
||||||
], onChanged: (path) {
|
DropdownMenuItem(
|
||||||
if (path is String && path.isNotEmpty){
|
child: Text('/'),
|
||||||
model.openDirectory(path, isLocal: isLocal);
|
value: '/',
|
||||||
}
|
)
|
||||||
})
|
],
|
||||||
),
|
onChanged: (path) {
|
||||||
|
if (path is String && path.isNotEmpty) {
|
||||||
|
model.openDirectory(path, isLocal: isLocal);
|
||||||
|
}
|
||||||
|
})),
|
||||||
controller: TextEditingController(
|
controller: TextEditingController(
|
||||||
text: isLocal
|
text: isLocal
|
||||||
? model.currentLocalDir.path
|
? model.currentLocalDir.path
|
||||||
|
@ -16,10 +16,10 @@ import 'package:wakelock/wakelock.dart';
|
|||||||
// import 'package:window_manager/window_manager.dart';
|
// import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../consts.dart';
|
|
||||||
import '../../mobile/widgets/dialog.dart';
|
import '../../mobile/widgets/dialog.dart';
|
||||||
import '../../mobile/widgets/overlay.dart';
|
import '../../mobile/widgets/overlay.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
|
|
||||||
final initText = '\1' * 1024;
|
final initText = '\1' * 1024;
|
||||||
|
|
||||||
@ -59,8 +59,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
var ffitmp = FFI();
|
var ffitmp = FFI();
|
||||||
ffitmp.canvasModel.tabBarHeight = super.widget.tabBarHeight;
|
ffitmp.canvasModel.tabBarHeight = super.widget.tabBarHeight;
|
||||||
final ffi = Get.put(ffitmp, tag: widget.id);
|
final ffi = Get.put(ffitmp, tag: widget.id);
|
||||||
// note: a little trick
|
|
||||||
ffi.ffiModel.platformFFI = gFFI.ffiModel.platformFFI;
|
|
||||||
ffi.connect(widget.id, tabBarHeight: super.widget.tabBarHeight);
|
ffi.connect(widget.id, tabBarHeight: super.widget.tabBarHeight);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||||||
@ -157,7 +155,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) {
|
||||||
_ffi.bind.sessionInputString(id: widget.id, value: s);
|
bind.sessionInputString(id: widget.id, value: s);
|
||||||
} else {
|
} else {
|
||||||
inputChar(s);
|
inputChar(s);
|
||||||
}
|
}
|
||||||
@ -191,11 +189,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
|
||||||
_ffi.bind.sessionInputString(id: widget.id, value: content);
|
bind.sessionInputString(id: widget.id, value: content);
|
||||||
openKeyboard();
|
openKeyboard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ffi.bind.sessionInputString(id: widget.id, value: content);
|
bind.sessionInputString(id: widget.id, value: content);
|
||||||
} else {
|
} else {
|
||||||
inputChar(content);
|
inputChar(content);
|
||||||
}
|
}
|
||||||
@ -509,8 +507,8 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
id: widget.id,
|
id: widget.id,
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
final cursor = _ffi.bind
|
final cursor = bind.getSessionToggleOptionSync(
|
||||||
.getSessionToggleOptionSync(id: widget.id, arg: 'show-remote-cursor');
|
id: widget.id, arg: 'show-remote-cursor');
|
||||||
if (keyboard || cursor) {
|
if (keyboard || cursor) {
|
||||||
paints.add(CursorPaint(
|
paints.add(CursorPaint(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
@ -519,10 +517,10 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
paints.add(getHelpTools());
|
paints.add(getHelpTools());
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (evt) {
|
onEnter: (evt) {
|
||||||
_ffi.bind.hostStopSystemKeyPropagate(stopped: false);
|
bind.hostStopSystemKeyPropagate(stopped: false);
|
||||||
},
|
},
|
||||||
onExit: (evt) {
|
onExit: (evt) {
|
||||||
_ffi.bind.hostStopSystemKeyPropagate(stopped: true);
|
bind.hostStopSystemKeyPropagate(stopped: true);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: MyTheme.canvasColor,
|
color: MyTheme.canvasColor,
|
||||||
@ -601,7 +599,7 @@ 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' &&
|
||||||
await _ffi.bind.getSessionToggleOption(id: id, arg: 'privacy-mode') !=
|
await bind.getSessionToggleOption(id: id, arg: 'privacy-mode') !=
|
||||||
true) {
|
true) {
|
||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Text(translate((_ffi.ffiModel.inputBlocked ? 'Unb' : 'B') +
|
child: Text(translate((_ffi.ffiModel.inputBlocked ? 'Unb' : 'B') +
|
||||||
@ -617,28 +615,27 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
elevation: 8,
|
elevation: 8,
|
||||||
);
|
);
|
||||||
if (value == 'cad') {
|
if (value == 'cad') {
|
||||||
_ffi.bind.sessionCtrlAltDel(id: widget.id);
|
bind.sessionCtrlAltDel(id: widget.id);
|
||||||
} else if (value == 'lock') {
|
} else if (value == 'lock') {
|
||||||
_ffi.bind.sessionLockScreen(id: widget.id);
|
bind.sessionLockScreen(id: widget.id);
|
||||||
} else if (value == 'block-input') {
|
} else if (value == 'block-input') {
|
||||||
_ffi.bind.sessionToggleOption(
|
bind.sessionToggleOption(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
value: (_ffi.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
|
value: (_ffi.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
|
||||||
_ffi.ffiModel.inputBlocked = !_ffi.ffiModel.inputBlocked;
|
_ffi.ffiModel.inputBlocked = !_ffi.ffiModel.inputBlocked;
|
||||||
} else if (value == 'refresh') {
|
} else if (value == 'refresh') {
|
||||||
_ffi.bind.sessionRefresh(id: widget.id);
|
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) {
|
||||||
_ffi.bind.sessionInputString(id: widget.id, value: data.text ?? "");
|
bind.sessionInputString(id: widget.id, value: data.text ?? "");
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
} else if (value == 'enter_os_password') {
|
} else if (value == 'enter_os_password') {
|
||||||
var password =
|
var password = await bind.getSessionOption(id: id, arg: "os-password");
|
||||||
await _ffi.bind.getSessionOption(id: id, arg: "os-password");
|
|
||||||
if (password != null) {
|
if (password != null) {
|
||||||
_ffi.bind.sessionInputOsPassword(id: widget.id, value: password);
|
bind.sessionInputOsPassword(id: widget.id, value: password);
|
||||||
} else {
|
} else {
|
||||||
showSetOSPassword(widget.id, true);
|
showSetOSPassword(widget.id, true);
|
||||||
}
|
}
|
||||||
@ -666,7 +663,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
onTouchModeChange: (t) {
|
onTouchModeChange: (t) {
|
||||||
_ffi.ffiModel.toggleTouchMode();
|
_ffi.ffiModel.toggleTouchMode();
|
||||||
final v = _ffi.ffiModel.touchMode ? 'Y' : '';
|
final v = _ffi.ffiModel.touchMode ? 'Y' : '';
|
||||||
_ffi.bind.sessionPeerOption(
|
bind.sessionPeerOption(
|
||||||
id: widget.id, name: "touch-mode", value: v);
|
id: widget.id, name: "touch-mode", value: v);
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
@ -892,12 +889,12 @@ class ImagePainter extends CustomPainter {
|
|||||||
|
|
||||||
CheckboxListTile getToggle(
|
CheckboxListTile getToggle(
|
||||||
String id, void Function(void Function()) setState, option, name) {
|
String id, void Function(void Function()) setState, option, name) {
|
||||||
final opt = ffi(id).bind.getSessionToggleOptionSync(id: id, arg: option);
|
final opt = bind.getSessionToggleOptionSync(id: id, arg: option);
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
value: opt,
|
value: opt,
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
setState(() {
|
setState(() {
|
||||||
ffi(id).bind.sessionToggleOption(id: id, value: option);
|
bind.sessionToggleOption(id: id, value: option);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
dense: true,
|
dense: true,
|
||||||
@ -917,11 +914,10 @@ RadioListTile<String> getRadio(String name, String toValue, String curValue,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showOptions(String id) async {
|
void showOptions(String id) async {
|
||||||
String quality =
|
String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced';
|
||||||
await ffi(id).bind.getSessionImageQuality(id: id) ?? 'balanced';
|
|
||||||
if (quality == '') quality = 'balanced';
|
if (quality == '') quality = 'balanced';
|
||||||
String viewStyle =
|
String viewStyle =
|
||||||
await ffi(id).bind.getSessionOption(id: id, arg: 'view-style') ?? '';
|
await bind.getSessionOption(id: id, arg: 'view-style') ?? '';
|
||||||
var displays = <Widget>[];
|
var displays = <Widget>[];
|
||||||
final pi = ffi(id).ffiModel.pi;
|
final pi = ffi(id).ffiModel.pi;
|
||||||
final image = ffi(id).ffiModel.getConnectionImage();
|
final image = ffi(id).ffiModel.getConnectionImage();
|
||||||
@ -934,7 +930,7 @@ void showOptions(String id) async {
|
|||||||
children.add(InkWell(
|
children.add(InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (i == cur) return;
|
if (i == cur) return;
|
||||||
ffi(id).bind.sessionSwitchDisplay(id: id, value: i);
|
bind.sessionSwitchDisplay(id: id, value: i);
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
child: Ink(
|
child: Ink(
|
||||||
@ -979,16 +975,14 @@ void showOptions(String id) async {
|
|||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
quality = value;
|
quality = value;
|
||||||
ffi(id).bind.sessionSetImageQuality(id: id, value: 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;
|
||||||
ffi(id)
|
bind.sessionPeerOption(id: id, name: "view-style", value: value);
|
||||||
.bind
|
|
||||||
.sessionPeerOption(id: id, name: "view-style", value: value);
|
|
||||||
ffi(id).canvasModel.updateViewStyle();
|
ffi(id).canvasModel.updateViewStyle();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -1018,10 +1012,8 @@ void showOptions(String id) async {
|
|||||||
|
|
||||||
void showSetOSPassword(String id, bool login) async {
|
void showSetOSPassword(String id, bool login) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var password =
|
var password = await bind.getSessionOption(id: id, arg: "os-password") ?? "";
|
||||||
await ffi(id).bind.getSessionOption(id: id, arg: "os-password") ?? "";
|
var autoLogin = await bind.getSessionOption(id: id, arg: "auto-login") != "";
|
||||||
var autoLogin =
|
|
||||||
await ffi(id).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(
|
||||||
@ -1054,13 +1046,11 @@ void showSetOSPassword(String id, bool login) async {
|
|||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
var text = controller.text.trim();
|
var text = controller.text.trim();
|
||||||
ffi(id)
|
bind.sessionPeerOption(id: id, name: "os-password", value: text);
|
||||||
.bind
|
bind.sessionPeerOption(
|
||||||
.sessionPeerOption(id: id, name: "os-password", value: text);
|
|
||||||
ffi(id).bind.sessionPeerOption(
|
|
||||||
id: id, name: "auto-login", value: autoLogin ? 'Y' : '');
|
id: id, name: "auto-login", value: autoLogin ? 'Y' : '');
|
||||||
if (text != "" && login) {
|
if (text != "" && login) {
|
||||||
ffi(id).bind.sessionInputOsPassword(id: id, value: text);
|
bind.sessionInputOsPassword(id: id, value: text);
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -8,6 +7,7 @@ import 'package:visibility_detector/visibility_detector.dart';
|
|||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import 'peercard_widget.dart';
|
import 'peercard_widget.dart';
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
if (!setEquals(_curPeers, _lastQueryPeers)) {
|
if (!setEquals(_curPeers, _lastQueryPeers)) {
|
||||||
if (now.difference(_lastChangeTime) > Duration(seconds: 1)) {
|
if (now.difference(_lastChangeTime) > Duration(seconds: 1)) {
|
||||||
if (_curPeers.length > 0) {
|
if (_curPeers.length > 0) {
|
||||||
gFFI.ffiModel.platformFFI.ffiBind
|
platformFFI.ffiBind
|
||||||
.queryOnlines(ids: _curPeers.toList(growable: false));
|
.queryOnlines(ids: _curPeers.toList(growable: false));
|
||||||
_lastQueryPeers = {..._curPeers};
|
_lastQueryPeers = {..._curPeers};
|
||||||
_lastQueryTime = DateTime.now();
|
_lastQueryTime = DateTime.now();
|
||||||
@ -127,7 +127,7 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
if (_queryCoun < _maxQueryCount) {
|
if (_queryCoun < _maxQueryCount) {
|
||||||
if (now.difference(_lastQueryTime) > Duration(seconds: 20)) {
|
if (now.difference(_lastQueryTime) > Duration(seconds: 20)) {
|
||||||
if (_curPeers.length > 0) {
|
if (_curPeers.length > 0) {
|
||||||
gFFI.ffiModel.platformFFI.ffiBind
|
platformFFI.ffiBind
|
||||||
.queryOnlines(ids: _curPeers.toList(growable: false));
|
.queryOnlines(ids: _curPeers.toList(growable: false));
|
||||||
_lastQueryTime = DateTime.now();
|
_lastQueryTime = DateTime.now();
|
||||||
_queryCoun += 1;
|
_queryCoun += 1;
|
||||||
@ -169,7 +169,7 @@ class RecentPeerWidget extends BasePeerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final widget = super.build(context);
|
final widget = super.build(context);
|
||||||
gFFI.bind.mainLoadRecentPeers();
|
bind.mainLoadRecentPeers();
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +186,7 @@ class FavoritePeerWidget extends BasePeerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final widget = super.build(context);
|
final widget = super.build(context);
|
||||||
gFFI.bind.mainLoadFavPeers();
|
bind.mainLoadFavPeers();
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ class DiscoveredPeerWidget extends BasePeerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final widget = super.build(context);
|
final widget = super.build(context);
|
||||||
gFFI.bind.mainLoadLanPeers();
|
bind.mainLoadLanPeers();
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,11 @@ import 'package:get/get.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
|
|
||||||
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
||||||
|
|
||||||
enum PeerType { recent, fav, discovered, ab }
|
enum PeerType { recent, fav, discovered, ab }
|
||||||
|
|
||||||
class _PeerCard extends StatefulWidget {
|
class _PeerCard extends StatefulWidget {
|
||||||
@ -15,10 +17,11 @@ class _PeerCard extends StatefulWidget {
|
|||||||
final PopupMenuItemsFunc popupMenuItemsFunc;
|
final PopupMenuItemsFunc popupMenuItemsFunc;
|
||||||
final PeerType type;
|
final PeerType type;
|
||||||
|
|
||||||
_PeerCard({required this.peer,
|
_PeerCard(
|
||||||
required this.popupMenuItemsFunc,
|
{required this.peer,
|
||||||
Key? key,
|
required this.popupMenuItemsFunc,
|
||||||
required this.type})
|
Key? key,
|
||||||
|
required this.type})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -54,9 +57,10 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPeerTile(BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
Widget _buildPeerTile(
|
||||||
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => Container(
|
() => Container(
|
||||||
decoration: deco.value,
|
decoration: deco.value,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -135,7 +139,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 5,
|
radius: 5,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
peer.online ? Colors.green : Colors.yellow)),
|
peer.online ? Colors.green : Colors.yellow)),
|
||||||
Text('${peer.id}')
|
Text('${peer.id}')
|
||||||
]),
|
]),
|
||||||
InkWell(
|
InkWell(
|
||||||
@ -183,12 +187,13 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
);
|
);
|
||||||
if (value == 'remove') {
|
if (value == 'remove') {
|
||||||
setState(() => gFFI.setByName('remove', '$id'));
|
setState(() => gFFI.setByName('remove', '$id'));
|
||||||
() async {
|
() async {
|
||||||
removePreference(id);
|
removePreference(id);
|
||||||
}();
|
}();
|
||||||
} else if (value == 'file') {
|
} else if (value == 'file') {
|
||||||
_connect(id, isFileTransfer: true);
|
_connect(id, isFileTransfer: true);
|
||||||
} else if (value == 'add-fav') {} else if (value == 'connect') {
|
} else if (value == 'add-fav') {
|
||||||
|
} else if (value == 'connect') {
|
||||||
_connect(id, isFileTransfer: false);
|
_connect(id, isFileTransfer: false);
|
||||||
} else if (value == 'ab-delete') {
|
} else if (value == 'ab-delete') {
|
||||||
gFFI.abModel.deletePeer(id);
|
gFFI.abModel.deletePeer(id);
|
||||||
@ -199,7 +204,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
} else if (value == 'rename') {
|
} else if (value == 'rename') {
|
||||||
_rename(id);
|
_rename(id);
|
||||||
} else if (value == 'unremember-password') {
|
} else if (value == 'unremember-password') {
|
||||||
await gFFI.bind.mainForgetPassword(id: id);
|
await bind.mainForgetPassword(id: id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +225,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => Container(
|
() => Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: rxTags.contains(tagName) ? Colors.blue : null,
|
color: rxTags.contains(tagName) ? Colors.blue : null,
|
||||||
border: Border.all(color: MyTheme.darkGray),
|
border: Border.all(color: MyTheme.darkGray),
|
||||||
@ -264,12 +269,12 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
child: Wrap(
|
child: Wrap(
|
||||||
children: tags
|
children: tags
|
||||||
.map((e) => _buildTag(e, selectedTag, onTap: () {
|
.map((e) => _buildTag(e, selectedTag, onTap: () {
|
||||||
if (selectedTag.contains(e)) {
|
if (selectedTag.contains(e)) {
|
||||||
selectedTag.remove(e);
|
selectedTag.remove(e);
|
||||||
} else {
|
} else {
|
||||||
selectedTag.add(e);
|
selectedTag.add(e);
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.toList(growable: false),
|
.toList(growable: false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -13,6 +13,8 @@ import 'package:provider/provider.dart';
|
|||||||
// import 'package:window_manager/window_manager.dart';
|
// import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import 'common.dart';
|
import 'common.dart';
|
||||||
|
import 'consts.dart';
|
||||||
|
import 'models/platform_model.dart';
|
||||||
import 'mobile/pages/home_page.dart';
|
import 'mobile/pages/home_page.dart';
|
||||||
import 'mobile/pages/server_page.dart';
|
import 'mobile/pages/server_page.dart';
|
||||||
import 'mobile/pages/settings_page.dart';
|
import 'mobile/pages/settings_page.dart';
|
||||||
@ -21,25 +23,9 @@ int? windowId;
|
|||||||
|
|
||||||
Future<Null> main(List<String> args) async {
|
Future<Null> main(List<String> args) async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
// global FFI, use this **ONLY** for global configuration
|
|
||||||
// for convenience, use global FFI on mobile platform
|
|
||||||
// focus on multi-ffi on desktop first
|
|
||||||
await initGlobalFFI();
|
|
||||||
// await Firebase.initializeApp();
|
|
||||||
if (isAndroid) {
|
|
||||||
toAndroidChannelInit();
|
|
||||||
}
|
|
||||||
refreshCurrentUser();
|
|
||||||
runRustDeskApp(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThemeData getCurrentTheme() {
|
|
||||||
return isDarkTheme() ? MyTheme.darkTheme : MyTheme.darkTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
void runRustDeskApp(List<String> args) async {
|
|
||||||
if (!isDesktop) {
|
if (!isDesktop) {
|
||||||
runApp(App());
|
runMainApp(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// main window
|
// main window
|
||||||
@ -52,28 +38,62 @@ void runRustDeskApp(List<String> args) async {
|
|||||||
WindowType wType = type.windowType;
|
WindowType wType = type.windowType;
|
||||||
switch (wType) {
|
switch (wType) {
|
||||||
case WindowType.RemoteDesktop:
|
case WindowType.RemoteDesktop:
|
||||||
runApp(GetMaterialApp(
|
runRemoteScreen(argument);
|
||||||
theme: getCurrentTheme(),
|
|
||||||
home: DesktopRemoteScreen(
|
|
||||||
params: argument,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
break;
|
break;
|
||||||
case WindowType.FileTransfer:
|
case WindowType.FileTransfer:
|
||||||
runApp(GetMaterialApp(
|
runFileTransferScreen(argument);
|
||||||
theme: getCurrentTheme(),
|
|
||||||
home: DesktopFileTransferScreen(params: argument)));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
runMainApp(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeData getCurrentTheme() {
|
||||||
|
return isDarkTheme() ? MyTheme.darkTheme : MyTheme.darkTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initEnv(String appType) async {
|
||||||
|
await platformFFI.init(appType);
|
||||||
|
// global FFI, use this **ONLY** for global configuration
|
||||||
|
// for convenience, use global FFI on mobile platform
|
||||||
|
// focus on multi-ffi on desktop first
|
||||||
|
await initGlobalFFI();
|
||||||
|
// await Firebase.initializeApp();
|
||||||
|
if (isAndroid) {
|
||||||
|
toAndroidChannelInit();
|
||||||
|
}
|
||||||
|
refreshCurrentUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
void runMainApp(bool startService) async {
|
||||||
|
await initEnv(kAppTypeMain);
|
||||||
|
if (startService) {
|
||||||
// await windowManager.ensureInitialized();
|
// await windowManager.ensureInitialized();
|
||||||
// disable tray
|
// disable tray
|
||||||
// initTray();
|
// initTray();
|
||||||
gFFI.serverModel.startService();
|
gFFI.serverModel.startService();
|
||||||
runApp(App());
|
|
||||||
}
|
}
|
||||||
|
runApp(App());
|
||||||
|
}
|
||||||
|
|
||||||
|
void runRemoteScreen(Map<String, dynamic> argument) async {
|
||||||
|
await initEnv(kAppTypeDesktopRemote);
|
||||||
|
runApp(GetMaterialApp(
|
||||||
|
theme: getCurrentTheme(),
|
||||||
|
home: DesktopRemoteScreen(
|
||||||
|
params: argument,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void runFileTransferScreen(Map<String, dynamic> argument) async {
|
||||||
|
await initEnv(kAppTypeDesktopFileTransfer);
|
||||||
|
runApp(GetMaterialApp(
|
||||||
|
theme: getCurrentTheme(),
|
||||||
|
home: DesktopFileTransferScreen(params: argument)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends StatelessWidget {
|
class App extends StatelessWidget {
|
||||||
@ -108,8 +128,8 @@ class App extends StatelessWidget {
|
|||||||
builder: FlutterSmartDialog.init(
|
builder: FlutterSmartDialog.init(
|
||||||
builder: isAndroid
|
builder: isAndroid
|
||||||
? (_, child) => AccessibilityListener(
|
? (_, child) => AccessibilityListener(
|
||||||
child: child,
|
child: child,
|
||||||
)
|
)
|
||||||
: null)),
|
: null)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
import 'package:flutter_hbb/models/model.dart';
|
||||||
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ class AbModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getApiServer() async {
|
Future<String> getApiServer() async {
|
||||||
return await _ffi?.bind.mainGetApiServer() ?? "";
|
return await bind.mainGetApiServer() ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
|
@ -9,6 +9,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:path/path.dart' as Path;
|
import 'package:path/path.dart' as Path;
|
||||||
|
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
|
import 'platform_model.dart';
|
||||||
|
|
||||||
enum SortBy { Name, Type, Modified, Size }
|
enum SortBy { Name, Type, Modified, Size }
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
bool get localSortAscending => _localSortAscending;
|
bool get localSortAscending => _localSortAscending;
|
||||||
|
|
||||||
SortBy getSortStyle(bool isLocal){
|
SortBy getSortStyle(bool isLocal) {
|
||||||
return isLocal ? _localSortStyle : _remoteSortStyle;
|
return isLocal ? _localSortStyle : _remoteSortStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
// Desktop uses jobTable
|
// Desktop uses jobTable
|
||||||
// id = index + 1
|
// id = index + 1
|
||||||
final jobIndex = getJob(id);
|
final jobIndex = getJob(id);
|
||||||
if (jobIndex >= 0 && _jobTable.length > jobIndex){
|
if (jobIndex >= 0 && _jobTable.length > jobIndex) {
|
||||||
final job = _jobTable[jobIndex];
|
final job = _jobTable[jobIndex];
|
||||||
job.fileNum = int.parse(evt['file_num']);
|
job.fileNum = int.parse(evt['file_num']);
|
||||||
job.speed = double.parse(evt['speed']);
|
job.speed = double.parse(evt['speed']);
|
||||||
@ -203,8 +204,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
debugPrint("init remote home:${fd.path}");
|
debugPrint("init remote home:${fd.path}");
|
||||||
_currentRemoteDir = fd;
|
_currentRemoteDir = fd;
|
||||||
}
|
}
|
||||||
}
|
} finally {}
|
||||||
finally {}
|
|
||||||
}
|
}
|
||||||
_fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
_fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -260,7 +260,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
final id = int.tryParse(evt['id']) ?? 0;
|
final id = int.tryParse(evt['id']) ?? 0;
|
||||||
if (false == resp) {
|
if (false == resp) {
|
||||||
final jobIndex = getJob(id);
|
final jobIndex = getJob(id);
|
||||||
if (jobIndex != -1){
|
if (jobIndex != -1) {
|
||||||
cancelJob(id);
|
cancelJob(id);
|
||||||
final job = jobTable[jobIndex];
|
final job = jobTable[jobIndex];
|
||||||
job.state = JobState.done;
|
job.state = JobState.done;
|
||||||
@ -274,9 +274,12 @@ class FileModel extends ChangeNotifier {
|
|||||||
// overwrite
|
// overwrite
|
||||||
need_override = true;
|
need_override = true;
|
||||||
}
|
}
|
||||||
_ffi.target?.bind.sessionSetConfirmOverrideFile(id: _ffi.target?.id ?? "",
|
bind.sessionSetConfirmOverrideFile(
|
||||||
actId: id, fileNum: int.parse(evt['file_num']),
|
id: _ffi.target?.id ?? "",
|
||||||
needOverride: need_override, remember: fileConfirmCheckboxRemember,
|
actId: id,
|
||||||
|
fileNum: int.parse(evt['file_num']),
|
||||||
|
needOverride: need_override,
|
||||||
|
remember: fileConfirmCheckboxRemember,
|
||||||
isUpload: evt['is_upload'] == "true");
|
isUpload: evt['is_upload'] == "true");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,21 +291,27 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
onReady() async {
|
onReady() async {
|
||||||
_localOption.home = _ffi.target?.getByName("get_home_dir") ?? "";
|
_localOption.home = _ffi.target?.getByName("get_home_dir") ?? "";
|
||||||
_localOption.showHidden = (await _ffi.target?.bind.sessionGetPeerOption
|
_localOption.showHidden = (await bind.sessionGetPeerOption(
|
||||||
(id: _ffi.target?.id ?? "", name: "local_show_hidden"))?.isNotEmpty ?? false;
|
id: _ffi.target?.id ?? "", name: "local_show_hidden"))
|
||||||
|
?.isNotEmpty ??
|
||||||
|
false;
|
||||||
|
|
||||||
_remoteOption.showHidden = (await _ffi.target?.bind.sessionGetPeerOption
|
_remoteOption.showHidden = (await bind.sessionGetPeerOption(
|
||||||
(id: _ffi.target?.id ?? "", name: "remote_show_hidden"))?.isNotEmpty ?? false;
|
id: _ffi.target?.id ?? "", name: "remote_show_hidden"))
|
||||||
|
?.isNotEmpty ??
|
||||||
|
false;
|
||||||
_remoteOption.isWindows = _ffi.target?.ffiModel.pi.platform == "Windows";
|
_remoteOption.isWindows = _ffi.target?.ffiModel.pi.platform == "Windows";
|
||||||
|
|
||||||
debugPrint("remote platform: ${_ffi.target?.ffiModel.pi.platform}");
|
debugPrint("remote platform: ${_ffi.target?.ffiModel.pi.platform}");
|
||||||
|
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
|
||||||
final local = (await _ffi.target?.bind.sessionGetPeerOption
|
final local = (await bind.sessionGetPeerOption(
|
||||||
(id: _ffi.target?.id ?? "", name: "local_dir")) ?? "";
|
id: _ffi.target?.id ?? "", name: "local_dir")) ??
|
||||||
final remote = (await _ffi.target?.bind.sessionGetPeerOption
|
"";
|
||||||
(id: _ffi.target?.id ?? "", name: "remote_dir")) ?? "";
|
final remote = (await bind.sessionGetPeerOption(
|
||||||
|
id: _ffi.target?.id ?? "", name: "remote_dir")) ??
|
||||||
|
"";
|
||||||
openDirectory(local.isEmpty ? _localOption.home : local, isLocal: true);
|
openDirectory(local.isEmpty ? _localOption.home : local, isLocal: true);
|
||||||
openDirectory(remote.isEmpty ? _remoteOption.home : remote, isLocal: false);
|
openDirectory(remote.isEmpty ? _remoteOption.home : remote, isLocal: false);
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
@ -313,7 +322,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
openDirectory(_remoteOption.home, isLocal: false);
|
openDirectory(_remoteOption.home, isLocal: false);
|
||||||
}
|
}
|
||||||
// load last transfer jobs
|
// load last transfer jobs
|
||||||
await _ffi.target?.bind.sessionLoadLastTransferJobs(id: '${_ffi.target?.id}');
|
await bind.sessionLoadLastTransferJobs(id: '${_ffi.target?.id}');
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
@ -327,8 +336,8 @@ class FileModel extends ChangeNotifier {
|
|||||||
msgMap["remote_dir"] = _currentRemoteDir.path;
|
msgMap["remote_dir"] = _currentRemoteDir.path;
|
||||||
msgMap["remote_show_hidden"] = _remoteOption.showHidden ? "Y" : "";
|
msgMap["remote_show_hidden"] = _remoteOption.showHidden ? "Y" : "";
|
||||||
final id = _ffi.target?.id ?? "";
|
final id = _ffi.target?.id ?? "";
|
||||||
for(final msg in msgMap.entries) {
|
for (final msg in msgMap.entries) {
|
||||||
_ffi.target?.bind.sessionPeerOption(id: id, name: msg.key, value: msg.value);
|
bind.sessionPeerOption(id: id, name: msg.key, value: msg.value);
|
||||||
}
|
}
|
||||||
_currentLocalDir.clear();
|
_currentLocalDir.clear();
|
||||||
_currentRemoteDir.clear();
|
_currentRemoteDir.clear();
|
||||||
@ -339,8 +348,9 @@ class FileModel extends ChangeNotifier {
|
|||||||
Future refresh({bool? isLocal}) async {
|
Future refresh({bool? isLocal}) async {
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
isLocal = isLocal ?? _isLocal;
|
isLocal = isLocal ?? _isLocal;
|
||||||
await isLocal ? openDirectory(currentLocalDir.path, isLocal: isLocal) :
|
await isLocal
|
||||||
openDirectory(currentRemoteDir.path, isLocal: isLocal);
|
? openDirectory(currentLocalDir.path, isLocal: isLocal)
|
||||||
|
: openDirectory(currentRemoteDir.path, isLocal: isLocal);
|
||||||
} else {
|
} else {
|
||||||
await openDirectory(currentDir.path);
|
await openDirectory(currentDir.path);
|
||||||
}
|
}
|
||||||
@ -353,7 +363,9 @@ class FileModel extends ChangeNotifier {
|
|||||||
final isWindows =
|
final isWindows =
|
||||||
isLocal ? _localOption.isWindows : _remoteOption.isWindows;
|
isLocal ? _localOption.isWindows : _remoteOption.isWindows;
|
||||||
// process /C:\ -> C:\ on Windows
|
// process /C:\ -> C:\ on Windows
|
||||||
if (isLocal ? _localOption.isWindows : _remoteOption.isWindows && path.length > 1 && path[0] == '/') {
|
if (isLocal
|
||||||
|
? _localOption.isWindows
|
||||||
|
: _remoteOption.isWindows && path.length > 1 && path[0] == '/') {
|
||||||
path = path.substring(1);
|
path = path.substring(1);
|
||||||
if (path[path.length - 1] != '\\') {
|
if (path[path.length - 1] != '\\') {
|
||||||
path = path + "\\";
|
path = path + "\\";
|
||||||
@ -380,7 +392,8 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
goToParentDirectory({bool? isLocal}) {
|
goToParentDirectory({bool? isLocal}) {
|
||||||
isLocal = isLocal ?? _isLocal;
|
isLocal = isLocal ?? _isLocal;
|
||||||
final isWindows = isLocal ? _localOption.isWindows : _remoteOption.isWindows;
|
final isWindows =
|
||||||
|
isLocal ? _localOption.isWindows : _remoteOption.isWindows;
|
||||||
final currDir = isLocal ? currentLocalDir : currentRemoteDir;
|
final currDir = isLocal ? currentLocalDir : currentRemoteDir;
|
||||||
var parent = PathUtil.dirname(currDir.path, isWindows);
|
var parent = PathUtil.dirname(currDir.path, isWindows);
|
||||||
// specially for C:\, D:\, goto '/'
|
// specially for C:\, D:\, goto '/'
|
||||||
@ -395,12 +408,11 @@ class FileModel extends ChangeNotifier {
|
|||||||
sendFiles(SelectedItems items, {bool isRemote = false}) {
|
sendFiles(SelectedItems items, {bool isRemote = false}) {
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
// desktop sendFiles
|
// desktop sendFiles
|
||||||
final toPath =
|
final toPath = isRemote ? currentLocalDir.path : currentRemoteDir.path;
|
||||||
isRemote ? currentLocalDir.path : currentRemoteDir.path;
|
|
||||||
final isWindows =
|
final isWindows =
|
||||||
isRemote ? _localOption.isWindows : _remoteOption.isWindows;
|
isRemote ? _localOption.isWindows : _remoteOption.isWindows;
|
||||||
final showHidden =
|
final showHidden =
|
||||||
isRemote ? _localOption.showHidden : _remoteOption.showHidden;
|
isRemote ? _localOption.showHidden : _remoteOption.showHidden;
|
||||||
items.items.forEach((from) async {
|
items.items.forEach((from) async {
|
||||||
final jobId = ++_jobId;
|
final jobId = ++_jobId;
|
||||||
_jobTable.add(JobProgress()
|
_jobTable.add(JobProgress()
|
||||||
@ -408,11 +420,17 @@ class FileModel extends ChangeNotifier {
|
|||||||
..totalSize = from.size
|
..totalSize = from.size
|
||||||
..state = JobState.inProgress
|
..state = JobState.inProgress
|
||||||
..id = jobId
|
..id = jobId
|
||||||
..isRemote = isRemote
|
..isRemote = isRemote);
|
||||||
);
|
bind.sessionSendFiles(
|
||||||
_ffi.target?.bind.sessionSendFiles(id: '${_ffi.target?.id}', actId: _jobId, path: from.path, to: PathUtil.join(toPath, from.name, isWindows)
|
id: '${_ffi.target?.id}',
|
||||||
,fileNum: 0, includeHidden: showHidden, isRemote: isRemote);
|
actId: _jobId,
|
||||||
print("path:${from.path}, toPath:${toPath}, to:${PathUtil.join(toPath, from.name, isWindows)}");
|
path: from.path,
|
||||||
|
to: PathUtil.join(toPath, from.name, isWindows),
|
||||||
|
fileNum: 0,
|
||||||
|
includeHidden: showHidden,
|
||||||
|
isRemote: isRemote);
|
||||||
|
print(
|
||||||
|
"path:${from.path}, toPath:${toPath}, to:${PathUtil.join(toPath, from.name, isWindows)}");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (items.isLocal == null) {
|
if (items.isLocal == null) {
|
||||||
@ -421,15 +439,21 @@ class FileModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
_jobProgress.state = JobState.inProgress;
|
_jobProgress.state = JobState.inProgress;
|
||||||
final toPath =
|
final toPath =
|
||||||
items.isLocal! ? currentRemoteDir.path : currentLocalDir.path;
|
items.isLocal! ? currentRemoteDir.path : currentLocalDir.path;
|
||||||
final isWindows =
|
final isWindows =
|
||||||
items.isLocal! ? _localOption.isWindows : _remoteOption.isWindows;
|
items.isLocal! ? _localOption.isWindows : _remoteOption.isWindows;
|
||||||
final showHidden =
|
final showHidden =
|
||||||
items.isLocal! ? _localOption.showHidden : _remoteOption.showHidden;
|
items.isLocal! ? _localOption.showHidden : _remoteOption.showHidden;
|
||||||
items.items.forEach((from) async {
|
items.items.forEach((from) async {
|
||||||
_jobId++;
|
_jobId++;
|
||||||
await _ffi.target?.bind.sessionSendFiles(id: '${_ffi.target?.getId()}', actId: _jobId, path: from.path, to: PathUtil.join(toPath, from.name, isWindows)
|
await bind.sessionSendFiles(
|
||||||
,fileNum: 0, includeHidden: showHidden, isRemote: !(items.isLocal!));
|
id: '${_ffi.target?.getId()}',
|
||||||
|
actId: _jobId,
|
||||||
|
path: from.path,
|
||||||
|
to: PathUtil.join(toPath, from.name, isWindows),
|
||||||
|
fileNum: 0,
|
||||||
|
includeHidden: showHidden,
|
||||||
|
isRemote: !(items.isLocal!));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -626,21 +650,34 @@ class FileModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendRemoveFile(String path, int fileNum, bool isLocal) {
|
sendRemoveFile(String path, int fileNum, bool isLocal) {
|
||||||
_ffi.target?.bind.sessionRemoveFile(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal, fileNum: fileNum);
|
bind.sessionRemoveFile(
|
||||||
|
id: '${_ffi.target?.id}',
|
||||||
|
actId: _jobId,
|
||||||
|
path: path,
|
||||||
|
isRemote: !isLocal,
|
||||||
|
fileNum: fileNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendRemoveEmptyDir(String path, int fileNum, bool isLocal) {
|
sendRemoveEmptyDir(String path, int fileNum, bool isLocal) {
|
||||||
_ffi.target?.bind.sessionRemoveAllEmptyDirs(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal);
|
bind.sessionRemoveAllEmptyDirs(
|
||||||
|
id: '${_ffi.target?.id}',
|
||||||
|
actId: _jobId,
|
||||||
|
path: path,
|
||||||
|
isRemote: !isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
createDir(String path, {bool? isLocal}) async {
|
createDir(String path, {bool? isLocal}) async {
|
||||||
isLocal = isLocal ?? this.isLocal;
|
isLocal = isLocal ?? this.isLocal;
|
||||||
_jobId++;
|
_jobId++;
|
||||||
_ffi.target?.bind.sessionCreateDir(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal);
|
bind.sessionCreateDir(
|
||||||
|
id: '${_ffi.target?.id}',
|
||||||
|
actId: _jobId,
|
||||||
|
path: path,
|
||||||
|
isRemote: !isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelJob(int id) async {
|
cancelJob(int id) async {
|
||||||
_ffi.target?.bind.sessionCancelJob(id: '${_ffi.target?.id}', actId: id);
|
bind.sessionCancelJob(id: '${_ffi.target?.id}', actId: id);
|
||||||
jobReset();
|
jobReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,14 +687,18 @@ class FileModel extends ChangeNotifier {
|
|||||||
// compatible for mobile logic
|
// compatible for mobile logic
|
||||||
_currentLocalDir.changeSortStyle(sort, ascending: ascending);
|
_currentLocalDir.changeSortStyle(sort, ascending: ascending);
|
||||||
_currentRemoteDir.changeSortStyle(sort, ascending: ascending);
|
_currentRemoteDir.changeSortStyle(sort, ascending: ascending);
|
||||||
_localSortStyle = sort; _localSortAscending = ascending;
|
_localSortStyle = sort;
|
||||||
_remoteSortStyle = sort; _remoteSortAscending = ascending;
|
_localSortAscending = ascending;
|
||||||
|
_remoteSortStyle = sort;
|
||||||
|
_remoteSortAscending = ascending;
|
||||||
} else if (isLocal) {
|
} else if (isLocal) {
|
||||||
_currentLocalDir.changeSortStyle(sort, ascending: ascending);
|
_currentLocalDir.changeSortStyle(sort, ascending: ascending);
|
||||||
_localSortStyle = sort; _localSortAscending = ascending;
|
_localSortStyle = sort;
|
||||||
|
_localSortAscending = ascending;
|
||||||
} else {
|
} else {
|
||||||
_currentRemoteDir.changeSortStyle(sort, ascending: ascending);
|
_currentRemoteDir.changeSortStyle(sort, ascending: ascending);
|
||||||
_remoteSortStyle = sort; _remoteSortAscending = ascending;
|
_remoteSortStyle = sort;
|
||||||
|
_remoteSortAscending = ascending;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -668,7 +709,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
void updateFolderFiles(Map<String, dynamic> evt) {
|
void updateFolderFiles(Map<String, dynamic> evt) {
|
||||||
// ret: "{\"id\":1,\"num_entries\":12,\"total_size\":1264822.0}"
|
// ret: "{\"id\":1,\"num_entries\":12,\"total_size\":1264822.0}"
|
||||||
Map<String,dynamic> info = json.decode(evt['info']);
|
Map<String, dynamic> info = json.decode(evt['info']);
|
||||||
int id = info['id'];
|
int id = info['id'];
|
||||||
int num_entries = info['num_entries'];
|
int num_entries = info['num_entries'];
|
||||||
double total_size = info['total_size'];
|
double total_size = info['total_size'];
|
||||||
@ -685,7 +726,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
void loadLastJob(Map<String, dynamic> evt) {
|
void loadLastJob(Map<String, dynamic> evt) {
|
||||||
debugPrint("load last job: ${evt}");
|
debugPrint("load last job: ${evt}");
|
||||||
Map<String,dynamic> jobDetail = json.decode(evt['value']);
|
Map<String, dynamic> jobDetail = json.decode(evt['value']);
|
||||||
// int id = int.parse(jobDetail['id']);
|
// int id = int.parse(jobDetail['id']);
|
||||||
String remote = jobDetail['remote'];
|
String remote = jobDetail['remote'];
|
||||||
String to = jobDetail['to'];
|
String to = jobDetail['to'];
|
||||||
@ -703,13 +744,14 @@ class FileModel extends ChangeNotifier {
|
|||||||
..showHidden = showHidden
|
..showHidden = showHidden
|
||||||
..state = JobState.paused;
|
..state = JobState.paused;
|
||||||
jobTable.add(jobProgress);
|
jobTable.add(jobProgress);
|
||||||
_ffi.target?.bind.sessionAddJob(id: '${_ffi.target?.id}',
|
bind.sessionAddJob(
|
||||||
isRemote: isRemote,
|
id: '${_ffi.target?.id}',
|
||||||
includeHidden: showHidden,
|
isRemote: isRemote,
|
||||||
actId: currJobId,
|
includeHidden: showHidden,
|
||||||
path: isRemote ? remote : to,
|
actId: currJobId,
|
||||||
to: isRemote ? to: remote,
|
path: isRemote ? remote : to,
|
||||||
fileNum: fileNum,
|
to: isRemote ? to : remote,
|
||||||
|
fileNum: fileNum,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,9 +759,8 @@ class FileModel extends ChangeNotifier {
|
|||||||
final jobIndex = getJob(jobId);
|
final jobIndex = getJob(jobId);
|
||||||
if (jobIndex != -1) {
|
if (jobIndex != -1) {
|
||||||
final job = jobTable[jobIndex];
|
final job = jobTable[jobIndex];
|
||||||
_ffi.target?.bind.sessionResumeJob(id: '${_ffi.target?.id}',
|
bind.sessionResumeJob(
|
||||||
actId: job.id,
|
id: '${_ffi.target?.id}', actId: job.id, isRemote: job.isRemote);
|
||||||
isRemote: job.isRemote);
|
|
||||||
job.state = JobState.inProgress;
|
job.state = JobState.inProgress;
|
||||||
} else {
|
} else {
|
||||||
debugPrint("jobId ${jobId} is not exists");
|
debugPrint("jobId ${jobId} is not exists");
|
||||||
@ -844,12 +885,12 @@ class FileFetcher {
|
|||||||
String path, bool isLocal, bool showHidden) async {
|
String path, bool isLocal, bool showHidden) async {
|
||||||
try {
|
try {
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
final res = await _ffi.bind.sessionReadLocalDirSync(
|
final res = await bind.sessionReadLocalDirSync(
|
||||||
id: id ?? "", path: path, showHidden: showHidden);
|
id: id ?? "", path: path, showHidden: showHidden);
|
||||||
final fd = FileDirectory.fromJson(jsonDecode(res));
|
final fd = FileDirectory.fromJson(jsonDecode(res));
|
||||||
return fd;
|
return fd;
|
||||||
} else {
|
} else {
|
||||||
await _ffi.bind.sessionReadRemoteDir(
|
await bind.sessionReadRemoteDir(
|
||||||
id: id ?? "", path: path, includeHidden: showHidden);
|
id: id ?? "", path: path, includeHidden: showHidden);
|
||||||
return registerReadTask(isLocal, path);
|
return registerReadTask(isLocal, path);
|
||||||
}
|
}
|
||||||
@ -862,7 +903,12 @@ class FileFetcher {
|
|||||||
int id, String path, bool isLocal, bool showHidden) async {
|
int id, String path, bool isLocal, bool showHidden) async {
|
||||||
// TODO test Recursive is show hidden default?
|
// TODO test Recursive is show hidden default?
|
||||||
try {
|
try {
|
||||||
await _ffi.bind.sessionReadDirRecursive(id: _ffi.id, actId: id, path: path, isRemote: !isLocal, showHidden: showHidden);
|
await bind.sessionReadDirRecursive(
|
||||||
|
id: _ffi.id,
|
||||||
|
actId: id,
|
||||||
|
path: path,
|
||||||
|
isRemote: !isLocal,
|
||||||
|
showHidden: showHidden);
|
||||||
return registerReadRecursiveTask(id);
|
return registerReadRecursiveTask(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return Future.error(e);
|
return Future.error(e);
|
||||||
@ -1033,7 +1079,9 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
|
|||||||
files.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
files.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||||
|
|
||||||
// first folders will go to list (if available) then files will go to list.
|
// first folders will go to list (if available) then files will go to list.
|
||||||
return ascending ? [...dirs, ...files] : [...dirs.reversed.toList(), ...files.reversed.toList()];
|
return ascending
|
||||||
|
? [...dirs, ...files]
|
||||||
|
: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
||||||
} else if (sortType == SortBy.Modified) {
|
} else if (sortType == SortBy.Modified) {
|
||||||
// making the list of Path & DateTime
|
// making the list of Path & DateTime
|
||||||
List<_PathStat> _pathStat = [];
|
List<_PathStat> _pathStat = [];
|
||||||
@ -1065,7 +1113,9 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
|
|||||||
.split('.')
|
.split('.')
|
||||||
.last
|
.last
|
||||||
.compareTo(b.name.toLowerCase().split('.').last));
|
.compareTo(b.name.toLowerCase().split('.').last));
|
||||||
return ascending ? [...dirs, ...files]: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
return ascending
|
||||||
|
? [...dirs, ...files]
|
||||||
|
: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
||||||
} else if (sortType == SortBy.Size) {
|
} else if (sortType == SortBy.Size) {
|
||||||
// create list of path and size
|
// create list of path and size
|
||||||
Map<String, int> _sizeMap = {};
|
Map<String, int> _sizeMap = {};
|
||||||
@ -1090,7 +1140,9 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
|
|||||||
.indexWhere((element) => element.key == a.name)
|
.indexWhere((element) => element.key == a.name)
|
||||||
.compareTo(
|
.compareTo(
|
||||||
_sizeMapList.indexWhere((element) => element.key == b.name)));
|
_sizeMapList.indexWhere((element) => element.key == b.name)));
|
||||||
return ascending ? [...dirs, ...files]: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
return ascending
|
||||||
|
? [...dirs, ...files]
|
||||||
|
: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ import 'package:tuple/tuple.dart';
|
|||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
import '../mobile/widgets/dialog.dart';
|
import '../mobile/widgets/dialog.dart';
|
||||||
import '../mobile/widgets/overlay.dart';
|
import '../mobile/widgets/overlay.dart';
|
||||||
import 'native_model.dart' if (dart.library.html) 'web_model.dart';
|
|
||||||
import 'peer_model.dart';
|
import 'peer_model.dart';
|
||||||
|
import 'platform_model.dart';
|
||||||
|
|
||||||
typedef HandleMsgBox = void Function(Map<String, dynamic> evt, String id);
|
typedef HandleMsgBox = void Function(Map<String, dynamic> evt, String id);
|
||||||
bool _waitForImage = false;
|
bool _waitForImage = false;
|
||||||
@ -29,7 +29,6 @@ bool _waitForImage = false;
|
|||||||
class FfiModel with ChangeNotifier {
|
class FfiModel with ChangeNotifier {
|
||||||
PeerInfo _pi = PeerInfo();
|
PeerInfo _pi = PeerInfo();
|
||||||
Display _display = Display();
|
Display _display = Display();
|
||||||
PlatformFFI _platformFFI = PlatformFFI();
|
|
||||||
|
|
||||||
var _inputBlocked = false;
|
var _inputBlocked = false;
|
||||||
final _permissions = Map<String, bool>();
|
final _permissions = Map<String, bool>();
|
||||||
@ -44,12 +43,6 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
Display get display => _display;
|
Display get display => _display;
|
||||||
|
|
||||||
PlatformFFI get platformFFI => _platformFFI;
|
|
||||||
|
|
||||||
set platformFFI(PlatformFFI value) {
|
|
||||||
_platformFFI = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool? get secure => _secure;
|
bool? get secure => _secure;
|
||||||
|
|
||||||
bool? get direct => _direct;
|
bool? get direct => _direct;
|
||||||
@ -71,10 +64,6 @@ class FfiModel with ChangeNotifier {
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
await _platformFFI.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleTouchMode() {
|
void toggleTouchMode() {
|
||||||
if (!isPeerAndroid) {
|
if (!isPeerAndroid) {
|
||||||
_touchMode = !_touchMode;
|
_touchMode = !_touchMode;
|
||||||
@ -280,7 +269,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
if (hasRetry) {
|
if (hasRetry) {
|
||||||
_timer = Timer(Duration(seconds: _reconnects), () {
|
_timer = Timer(Duration(seconds: _reconnects), () {
|
||||||
parent.target?.bind.sessionReconnect(id: id);
|
bind.sessionReconnect(id: id);
|
||||||
clearPermissions();
|
clearPermissions();
|
||||||
showLoading(translate('Connecting...'));
|
showLoading(translate('Connecting...'));
|
||||||
});
|
});
|
||||||
@ -306,9 +295,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
Timer(Duration(milliseconds: 100), showMobileActionsOverlay);
|
Timer(Duration(milliseconds: 100), showMobileActionsOverlay);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_touchMode = await parent.target?.bind
|
_touchMode =
|
||||||
.getSessionOption(id: peerId, arg: "touch-mode") !=
|
await bind.getSessionOption(id: peerId, arg: "touch-mode") != '';
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt['is_file_transfer'] == "true") {
|
if (evt['is_file_transfer'] == "true") {
|
||||||
@ -387,8 +375,7 @@ class ImageModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
Future.delayed(Duration(milliseconds: 1), () {
|
Future.delayed(Duration(milliseconds: 1), () {
|
||||||
if (parent.target?.ffiModel.isPeerAndroid ?? false) {
|
if (parent.target?.ffiModel.isPeerAndroid ?? false) {
|
||||||
parent.target?.bind
|
bind.sessionPeerOption(id: _id, name: "view-style", value: "shrink");
|
||||||
.sessionPeerOption(id: _id, name: "view-style", value: "shrink");
|
|
||||||
parent.target?.canvasModel.updateViewStyle();
|
parent.target?.canvasModel.updateViewStyle();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -439,8 +426,7 @@ class CanvasModel with ChangeNotifier {
|
|||||||
double get tabBarHeight => _tabBarHeight;
|
double get tabBarHeight => _tabBarHeight;
|
||||||
|
|
||||||
void updateViewStyle() async {
|
void updateViewStyle() async {
|
||||||
final s =
|
final s = await bind.getSessionOption(id: id, arg: 'view-style');
|
||||||
await parent.target?.bind.getSessionOption(id: id, arg: 'view-style');
|
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -844,13 +830,6 @@ class FFI {
|
|||||||
this.userModel = UserModel(WeakReference(this));
|
this.userModel = UserModel(WeakReference(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
static FFI newFFI() {
|
|
||||||
final ffi = FFI();
|
|
||||||
// keep platformFFI only once
|
|
||||||
ffi.ffiModel.platformFFI = gFFI.ffiModel.platformFFI;
|
|
||||||
return ffi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the remote id for current client.
|
/// Get the remote id for current client.
|
||||||
String getId() {
|
String getId() {
|
||||||
return getByName('remote_id'); // TODO
|
return getByName('remote_id'); // TODO
|
||||||
@ -1008,16 +987,16 @@ 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 ffiModel.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 = '']) {
|
||||||
ffiModel.platformFFI.setByName(name, value);
|
platformFFI.setByName(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getOption(String name) {
|
String getOption(String name) {
|
||||||
return ffiModel.platformFFI.getByName("option", name);
|
return platformFFI.getByName("option", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getLocalOption(String name) {
|
Future<String> getLocalOption(String name) {
|
||||||
@ -1040,11 +1019,9 @@ class FFI {
|
|||||||
Map<String, String> res = Map()
|
Map<String, String> res = Map()
|
||||||
..["name"] = name
|
..["name"] = name
|
||||||
..["value"] = value;
|
..["value"] = value;
|
||||||
return ffiModel.platformFFI.setByName('option', jsonEncode(res));
|
return platformFFI.setByName('option', jsonEncode(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
RustdeskImpl get bind => ffiModel.platformFFI.ffiBind;
|
|
||||||
|
|
||||||
handleMouse(Map<String, dynamic> evt, {double tabBarHeight = 0.0}) {
|
handleMouse(Map<String, dynamic> evt, {double tabBarHeight = 0.0}) {
|
||||||
var type = '';
|
var type = '';
|
||||||
var isMove = false;
|
var isMove = false;
|
||||||
@ -1102,18 +1079,18 @@ class FFI {
|
|||||||
|
|
||||||
listenToMouse(bool yesOrNo) {
|
listenToMouse(bool yesOrNo) {
|
||||||
if (yesOrNo) {
|
if (yesOrNo) {
|
||||||
ffiModel.platformFFI.startDesktopWebListener();
|
platformFFI.startDesktopWebListener();
|
||||||
} else {
|
} else {
|
||||||
ffiModel.platformFFI.stopDesktopWebListener();
|
platformFFI.stopDesktopWebListener();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMethodCallHandler(FMethod callback) {
|
void setMethodCallHandler(FMethod callback) {
|
||||||
ffiModel.platformFFI.setMethodCallHandler(callback);
|
platformFFI.setMethodCallHandler(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> invokeMethod(String method, [dynamic arguments]) async {
|
Future<bool> invokeMethod(String method, [dynamic arguments]) async {
|
||||||
return await ffiModel.platformFFI.invokeMethod(method, arguments);
|
return await platformFFI.invokeMethod(method, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> getAudioInputs() async {
|
Future<List<String>> getAudioInputs() async {
|
||||||
|
@ -27,17 +27,24 @@ typedef HandleEvent = void Function(Map<String, dynamic> evt);
|
|||||||
/// FFI wrapper around the native Rust core.
|
/// FFI wrapper around the native Rust core.
|
||||||
/// Hides the platform differences.
|
/// Hides the platform differences.
|
||||||
class PlatformFFI {
|
class PlatformFFI {
|
||||||
Pointer<RgbaFrame>? _lastRgbaFrame;
|
|
||||||
String _dir = '';
|
String _dir = '';
|
||||||
String _homeDir = '';
|
String _homeDir = '';
|
||||||
F2? _getByName;
|
F2? _getByName;
|
||||||
F3? _setByName;
|
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;
|
||||||
void Function(Map<String, dynamic>)? _eventCallback;
|
void Function(Map<String, dynamic>)? _eventCallback;
|
||||||
|
|
||||||
|
PlatformFFI._();
|
||||||
|
|
||||||
|
static final PlatformFFI instance = PlatformFFI._();
|
||||||
|
final _toAndroidChannel = MethodChannel("mChannel");
|
||||||
|
|
||||||
RustdeskImpl get ffiBind => _ffiBind;
|
RustdeskImpl get ffiBind => _ffiBind;
|
||||||
|
|
||||||
|
static get localeName => Platform.localeName;
|
||||||
|
|
||||||
static Future<String> getVersion() async {
|
static Future<String> getVersion() async {
|
||||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
return packageInfo.version;
|
return packageInfo.version;
|
||||||
@ -94,10 +101,8 @@ class PlatformFFI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Init the FFI class, loads the native Rust core library.
|
/// Init the FFI class, loads the native Rust core library.
|
||||||
Future<Null> init() async {
|
Future<Null> init(String appType) async {
|
||||||
isIOS = Platform.isIOS;
|
_appType = appType;
|
||||||
isAndroid = Platform.isAndroid;
|
|
||||||
isDesktop = Platform.isWindows || Platform.isMacOS || Platform.isLinux;
|
|
||||||
// if (isDesktop) {
|
// if (isDesktop) {
|
||||||
// // TODO
|
// // TODO
|
||||||
// return;
|
// return;
|
||||||
@ -111,7 +116,7 @@ class PlatformFFI {
|
|||||||
: Platform.isMacOS
|
: Platform.isMacOS
|
||||||
? DynamicLibrary.open("librustdesk.dylib")
|
? DynamicLibrary.open("librustdesk.dylib")
|
||||||
: DynamicLibrary.process();
|
: DynamicLibrary.process();
|
||||||
print('initializing FFI');
|
debugPrint('initializing FFI ${_appType}');
|
||||||
try {
|
try {
|
||||||
_getByName = dylib.lookupFunction<F2, F2>('get_by_name');
|
_getByName = dylib.lookupFunction<F2, F2>('get_by_name');
|
||||||
_setByName =
|
_setByName =
|
||||||
@ -155,7 +160,8 @@ class PlatformFFI {
|
|||||||
name = macOsInfo.computerName;
|
name = macOsInfo.computerName;
|
||||||
id = macOsInfo.systemGUID ?? "";
|
id = macOsInfo.systemGUID ?? "";
|
||||||
}
|
}
|
||||||
print("info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir");
|
print(
|
||||||
|
"_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir");
|
||||||
setByName('info1', id);
|
setByName('info1', id);
|
||||||
setByName('info2', name);
|
setByName('info2', name);
|
||||||
setByName('home_dir', _homeDir);
|
setByName('home_dir', _homeDir);
|
||||||
@ -185,17 +191,18 @@ class PlatformFFI {
|
|||||||
/// Start listening to the Rust core's events and frames.
|
/// Start listening to the Rust core's events and frames.
|
||||||
void _startListenEvent(RustdeskImpl rustdeskImpl) {
|
void _startListenEvent(RustdeskImpl rustdeskImpl) {
|
||||||
() async {
|
() async {
|
||||||
await for (final message in rustdeskImpl.startGlobalEventStream()) {
|
await for (final message
|
||||||
if (_eventCallback != null) {
|
in rustdeskImpl.startGlobalEventStream(appType: _appType)) {
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> event = json.decode(message);
|
Map<String, dynamic> event = json.decode(message);
|
||||||
// _tryHandle here may be more flexible than _eventCallback
|
// _tryHandle here may be more flexible than _eventCallback
|
||||||
if (!_tryHandle(event)) {
|
if (!_tryHandle(event)) {
|
||||||
|
if (_eventCallback != null) {
|
||||||
_eventCallback!(event);
|
_eventCallback!(event);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
print('json.decode fail(): $e');
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('json.decode fail(): $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
@ -212,7 +219,7 @@ class PlatformFFI {
|
|||||||
void stopDesktopWebListener() {}
|
void stopDesktopWebListener() {}
|
||||||
|
|
||||||
void setMethodCallHandler(FMethod callback) {
|
void setMethodCallHandler(FMethod callback) {
|
||||||
toAndroidChannel.setMethodCallHandler((call) async {
|
_toAndroidChannel.setMethodCallHandler((call) async {
|
||||||
callback(call.method, call.arguments);
|
callback(call.method, call.arguments);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -220,9 +227,6 @@ class PlatformFFI {
|
|||||||
|
|
||||||
invokeMethod(String method, [dynamic arguments]) async {
|
invokeMethod(String method, [dynamic arguments]) async {
|
||||||
if (!isAndroid) return Future<bool>(() => false);
|
if (!isAndroid) return Future<bool>(() => false);
|
||||||
return await toAndroidChannel.invokeMethod(method, arguments);
|
return await _toAndroidChannel.invokeMethod(method, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final localeName = Platform.localeName;
|
|
||||||
final toAndroidChannel = MethodChannel("mChannel");
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import '../../common.dart';
|
import 'platform_model.dart';
|
||||||
|
|
||||||
class Peer {
|
class Peer {
|
||||||
final String id;
|
final String id;
|
||||||
@ -44,11 +44,10 @@ class Peers extends ChangeNotifier {
|
|||||||
_name = name;
|
_name = name;
|
||||||
_loadEvent = loadEvent;
|
_loadEvent = loadEvent;
|
||||||
_peers = _initPeers;
|
_peers = _initPeers;
|
||||||
gFFI.ffiModel.platformFFI.registerEventHandler(_cbQueryOnlines, _name,
|
platformFFI.registerEventHandler(_cbQueryOnlines, _name, (evt) {
|
||||||
(evt) {
|
|
||||||
_updateOnlineState(evt);
|
_updateOnlineState(evt);
|
||||||
});
|
});
|
||||||
gFFI.ffiModel.platformFFI.registerEventHandler(_loadEvent, _name, (evt) {
|
platformFFI.registerEventHandler(_loadEvent, _name, (evt) {
|
||||||
_updatePeers(evt);
|
_updatePeers(evt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -57,8 +56,8 @@ class Peers extends ChangeNotifier {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
gFFI.ffiModel.platformFFI.unregisterEventHandler(_cbQueryOnlines, _name);
|
platformFFI.unregisterEventHandler(_cbQueryOnlines, _name);
|
||||||
gFFI.ffiModel.platformFFI.unregisterEventHandler(_loadEvent, _name);
|
platformFFI.unregisterEventHandler(_loadEvent, _name);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
flutter/lib/models/platform_model.dart
Normal file
7
flutter/lib/models/platform_model.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:flutter_hbb/generated_bridge.dart';
|
||||||
|
import 'native_model.dart' if (dart.library.html) 'web_model.dart';
|
||||||
|
|
||||||
|
final platformFFI = PlatformFFI.instance;
|
||||||
|
final localeName = PlatformFFI.localeName;
|
||||||
|
|
||||||
|
RustdeskImpl get bind => platformFFI.ffiBind;
|
@ -6,6 +6,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
|
import 'platform_model.dart';
|
||||||
|
|
||||||
class UserModel extends ChangeNotifier {
|
class UserModel extends ChangeNotifier {
|
||||||
var userName = "".obs;
|
var userName = "".obs;
|
||||||
@ -17,8 +18,7 @@ class UserModel extends ChangeNotifier {
|
|||||||
if (userName.isNotEmpty) {
|
if (userName.isNotEmpty) {
|
||||||
return userName.value;
|
return userName.value;
|
||||||
}
|
}
|
||||||
final userInfo =
|
final userInfo = await bind.mainGetLocalOption(key: 'user_info') ?? "{}";
|
||||||
await parent.target?.bind.mainGetLocalOption(key: 'user_info') ?? "{}";
|
|
||||||
if (userInfo.trim().isEmpty) {
|
if (userInfo.trim().isEmpty) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -29,10 +29,6 @@ class UserModel extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> logOut() async {
|
Future<void> logOut() async {
|
||||||
debugPrint("start logout");
|
debugPrint("start logout");
|
||||||
final bind = parent.target?.bind;
|
|
||||||
if (bind == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final url = await bind.mainGetApiServer();
|
final url = await bind.mainGetApiServer();
|
||||||
final _ = await http.post(Uri.parse("$url/api/logout"),
|
final _ = await http.post(Uri.parse("$url/api/logout"),
|
||||||
body: {
|
body: {
|
||||||
@ -55,10 +51,6 @@ class UserModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> login(String userName, String pass) async {
|
Future<Map<String, dynamic>> login(String userName, String pass) async {
|
||||||
final bind = parent.target?.bind;
|
|
||||||
if (bind == null) {
|
|
||||||
return {"error": "no context"};
|
|
||||||
}
|
|
||||||
final url = await bind.mainGetApiServer();
|
final url = await bind.mainGetApiServer();
|
||||||
try {
|
try {
|
||||||
final resp = await http.post(Uri.parse("$url/api/login"),
|
final resp = await http.post(Uri.parse("$url/api/login"),
|
||||||
|
@ -20,7 +20,12 @@ class PlatformFFI {
|
|||||||
context.callMethod('setByName', [name, value]);
|
context.callMethod('setByName', [name, value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Null> init() async {
|
PlatformFFI._();
|
||||||
|
static final PlatformFFI instance = PlatformFFI._();
|
||||||
|
|
||||||
|
static get localeName => window.navigator.language;
|
||||||
|
|
||||||
|
static Future<Null> init(String _appType) async {
|
||||||
isWeb = true;
|
isWeb = true;
|
||||||
isWebDesktop = !context.callMethod('isMobile');
|
isWebDesktop = !context.callMethod('isMobile');
|
||||||
context.callMethod('init');
|
context.callMethod('init');
|
||||||
@ -68,5 +73,3 @@ class PlatformFFI {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final localeName = window.navigator.language;
|
|
||||||
|
@ -115,9 +115,9 @@ include(flutter/generated_plugins.cmake)
|
|||||||
# By default, "installing" just makes a relocatable bundle in the build
|
# By default, "installing" just makes a relocatable bundle in the build
|
||||||
# directory.
|
# directory.
|
||||||
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
#if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||||
endif()
|
#endif()
|
||||||
|
|
||||||
# Start with a clean build bundle directory every time.
|
# Start with a clean build bundle directory every time.
|
||||||
install(CODE "
|
install(CODE "
|
||||||
|
216
src/flutter.rs
216
src/flutter.rs
@ -31,10 +31,14 @@ use hbb_common::{
|
|||||||
use crate::common::make_fd_to_json;
|
use crate::common::make_fd_to_json;
|
||||||
use crate::{client::*, flutter_ffi::EventToUI, make_fd_flutter};
|
use crate::{client::*, flutter_ffi::EventToUI, make_fd_flutter};
|
||||||
|
|
||||||
|
pub(super) const APP_TYPE_MAIN: &str = "main";
|
||||||
|
pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote";
|
||||||
|
pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer";
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
// static ref SESSION: Arc<RwLock<Option<Session>>> = Default::default();
|
// static ref SESSION: Arc<RwLock<Option<Session>>> = Default::default();
|
||||||
pub static ref SESSIONS: RwLock<HashMap<String,Session>> = Default::default();
|
pub static ref SESSIONS: RwLock<HashMap<String,Session>> = Default::default();
|
||||||
pub static ref GLOBAL_EVENT_STREAM: RwLock<Option<StreamSink<String>>> = Default::default(); // rust to dart event channel
|
pub static ref GLOBAL_EVENT_STREAM: RwLock<HashMap<String, StreamSink<String>>> = Default::default(); // rust to dart event channel
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn get_session<'a>(id: &str) -> Option<&'a Session> {
|
// pub fn get_session<'a>(id: &str) -> Option<&'a Session> {
|
||||||
@ -786,113 +790,114 @@ impl Connection {
|
|||||||
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(message::Union::FileResponse(fr)) => match fr.union {
|
Some(message::Union::FileResponse(fr)) => {
|
||||||
Some(file_response::Union::Dir(fd)) => {
|
match fr.union {
|
||||||
let mut entries = fd.entries.to_vec();
|
Some(file_response::Union::Dir(fd)) => {
|
||||||
if self.session.peer_platform() == "Windows" {
|
let mut entries = fd.entries.to_vec();
|
||||||
fs::transform_windows_path(&mut entries);
|
if self.session.peer_platform() == "Windows" {
|
||||||
}
|
fs::transform_windows_path(&mut entries);
|
||||||
let id = fd.id;
|
}
|
||||||
self.session.push_event(
|
let id = fd.id;
|
||||||
"file_dir",
|
self.session.push_event(
|
||||||
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
"file_dir",
|
||||||
);
|
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
||||||
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
);
|
||||||
job.set_files(entries);
|
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
||||||
}
|
job.set_files(entries);
|
||||||
}
|
|
||||||
Some(file_response::Union::Block(block)) => {
|
|
||||||
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
|
||||||
if let Err(_err) = job.write(block, None).await {
|
|
||||||
// to-do: add "skip" for writing job
|
|
||||||
}
|
}
|
||||||
self.update_jobs_status();
|
|
||||||
}
|
}
|
||||||
}
|
Some(file_response::Union::Block(block)) => {
|
||||||
Some(file_response::Union::Done(d)) => {
|
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
if let Err(_err) = job.write(block, None).await {
|
||||||
job.modify_time();
|
// to-do: add "skip" for writing job
|
||||||
fs::remove_job(d.id, &mut self.write_jobs);
|
}
|
||||||
|
self.update_jobs_status();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.handle_job_status(d.id, d.file_num, None);
|
Some(file_response::Union::Done(d)) => {
|
||||||
}
|
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
||||||
Some(file_response::Union::Error(e)) => {
|
job.modify_time();
|
||||||
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
fs::remove_job(d.id, &mut self.write_jobs);
|
||||||
}
|
}
|
||||||
Some(file_response::Union::Digest(digest)) => {
|
self.handle_job_status(d.id, d.file_num, None);
|
||||||
if digest.is_upload {
|
}
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
Some(file_response::Union::Error(e)) => {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
||||||
let read_path = get_string(&job.join(&file.name));
|
}
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
Some(file_response::Union::Digest(digest)) => {
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
if digest.is_upload {
|
||||||
let req = FileTransferSendConfirmRequest {
|
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
||||||
id: digest.id,
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
file_num: digest.file_num,
|
let read_path = get_string(&job.join(&file.name));
|
||||||
union: Some(if overwrite {
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
} else {
|
let req = FileTransferSendConfirmRequest {
|
||||||
file_transfer_send_confirm_request::Union::Skip(
|
id: digest.id,
|
||||||
true,
|
file_num: digest.file_num,
|
||||||
)
|
union: Some(if overwrite {
|
||||||
}),
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
..Default::default()
|
} else {
|
||||||
};
|
file_transfer_send_confirm_request::Union::Skip(
|
||||||
job.confirm(&req);
|
true,
|
||||||
let msg = new_send_confirm(req);
|
)
|
||||||
allow_err!(peer.send(&msg).await);
|
}),
|
||||||
} else {
|
..Default::default()
|
||||||
self.handle_override_file_confirm(
|
};
|
||||||
digest.id,
|
job.confirm(&req);
|
||||||
digest.file_num,
|
let msg = new_send_confirm(req);
|
||||||
read_path,
|
allow_err!(peer.send(&msg).await);
|
||||||
true,
|
} else {
|
||||||
);
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
read_path,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
let write_path = get_string(&job.join(&file.name));
|
||||||
let write_path = get_string(&job.join(&file.name));
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
match fs::is_write_need_confirmation(&write_path, &digest) {
|
||||||
match fs::is_write_need_confirmation(&write_path, &digest) {
|
Ok(res) => match res {
|
||||||
Ok(res) => match res {
|
DigestCheckResult::IsSame => {
|
||||||
DigestCheckResult::IsSame => {
|
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
||||||
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.session.send_msg(msg);
|
|
||||||
}
|
|
||||||
DigestCheckResult::NeedConfirm(digest) => {
|
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
|
||||||
let msg = new_send_confirm(
|
|
||||||
FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
|
||||||
file_num: digest.file_num,
|
|
||||||
union: Some(if overwrite {
|
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
|
||||||
} else {
|
|
||||||
file_transfer_send_confirm_request::Union::Skip(true)
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
} else {
|
|
||||||
self.handle_override_file_confirm(
|
|
||||||
digest.id,
|
|
||||||
digest.file_num,
|
|
||||||
write_path.to_string(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
DigestCheckResult::NeedConfirm(digest) => {
|
||||||
DigestCheckResult::NoSuchFile => {
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
let msg = new_send_confirm(
|
let msg = new_send_confirm(
|
||||||
|
FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(if overwrite {
|
||||||
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
|
} else {
|
||||||
|
file_transfer_send_confirm_request::Union::Skip(true)
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.session.send_msg(msg);
|
||||||
|
} else {
|
||||||
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
write_path.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DigestCheckResult::NoSuchFile => {
|
||||||
|
let msg = new_send_confirm(
|
||||||
FileTransferSendConfirmRequest {
|
FileTransferSendConfirmRequest {
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
@ -900,19 +905,20 @@ impl Connection {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("error recving digest: {}", err);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
println!("error recving digest: {}", err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Some(message::Union::Misc(misc)) => match misc.union {
|
Some(message::Union::Misc(misc)) => match misc.union {
|
||||||
Some(misc::Union::AudioFormat(f)) => {
|
Some(misc::Union::AudioFormat(f)) => {
|
||||||
self.audio_handler.handle_format(f); //
|
self.audio_handler.handle_format(f); //
|
||||||
@ -1513,7 +1519,11 @@ pub mod connection_manager {
|
|||||||
assert!(h.get("name").is_none());
|
assert!(h.get("name").is_none());
|
||||||
h.insert("name", name);
|
h.insert("name", name);
|
||||||
|
|
||||||
if let Some(s) = GLOBAL_EVENT_STREAM.read().unwrap().as_ref() {
|
if let Some(s) = GLOBAL_EVENT_STREAM
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(super::APP_TYPE_MAIN)
|
||||||
|
{
|
||||||
s.add(serde_json::ser::to_string(&h).unwrap_or("".to_owned()));
|
s.add(serde_json::ser::to_string(&h).unwrap_or("".to_owned()));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -75,11 +75,17 @@ pub enum EventToUI {
|
|||||||
Rgba(ZeroCopyBuffer<Vec<u8>>),
|
Rgba(ZeroCopyBuffer<Vec<u8>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_global_event_stream(s: StreamSink<String>) -> ResultType<()> {
|
pub fn start_global_event_stream(s: StreamSink<String>, app_type: String) -> ResultType<()> {
|
||||||
let _ = flutter::GLOBAL_EVENT_STREAM.write().unwrap().insert(s);
|
if let Some(_) = flutter::GLOBAL_EVENT_STREAM.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) {
|
||||||
|
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) {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
crate::platform::windows::stop_system_key_propagate(stopped);
|
crate::platform::windows::stop_system_key_propagate(stopped);
|
||||||
@ -518,7 +524,7 @@ pub fn main_load_recent_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().as_ref() {
|
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()),
|
||||||
(
|
(
|
||||||
@ -544,7 +550,7 @@ pub fn main_load_fav_peers() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().as_ref() {
|
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()),
|
||||||
(
|
(
|
||||||
@ -558,7 +564,7 @@ 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().as_ref() {
|
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()),
|
||||||
@ -1066,7 +1072,7 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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().as_ref() {
|
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(",")),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user