This commit is contained in:
asur4s
2022-09-07 03:52:31 -04:00
43 changed files with 2863 additions and 3273 deletions

View File

@@ -209,10 +209,19 @@ class ChatModel with ChangeNotifier {
id: await bind.mainGetLastRemoteId(),
);
} else {
final client = _ffi.target?.serverModel.clients[id];
final client = _ffi.target?.serverModel.clients
.firstWhere((client) => client.id == id);
if (client == null) {
return debugPrint("Failed to receive msg,user doesn't exist");
}
if (isDesktop) {
window_on_top(null);
var index = _ffi.target?.serverModel.clients
.indexWhere((client) => client.id == id);
if (index != null && index >= 0) {
gFFI.serverModel.tabController.jumpTo(index);
}
}
chatUser = ChatUser(id: client.peerId, firstName: client.name);
}

View File

@@ -360,9 +360,9 @@ class FileModel extends ChangeNotifier {
Future refresh({bool? isLocal}) async {
if (isDesktop) {
isLocal = isLocal ?? _isLocal;
await isLocal
? openDirectory(currentLocalDir.path, isLocal: isLocal)
: openDirectory(currentRemoteDir.path, isLocal: isLocal);
isLocal
? await openDirectory(currentLocalDir.path, isLocal: isLocal)
: await openDirectory(currentRemoteDir.path, isLocal: isLocal);
} else {
await openDirectory(currentDir.path);
}
@@ -393,7 +393,7 @@ class FileModel extends ChangeNotifier {
}
notifyListeners();
} catch (e) {
debugPrint("Failed to openDirectory ${path} :$e");
debugPrint("Failed to openDirectory $path: $e");
}
}
@@ -559,49 +559,55 @@ class FileModel extends ChangeNotifier {
Future<bool?> showRemoveDialog(
String title, String content, bool showCheckbox) async {
return await parent.target?.dialogManager.show<bool>(
(setState, Function(bool v) close) => CustomAlertDialog(
title: Row(
children: [
Icon(Icons.warning, color: Colors.red),
SizedBox(width: 20),
Text(title)
],
),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(content),
SizedBox(height: 5),
Text(translate("This is irreversible!"),
style: TextStyle(fontWeight: FontWeight.bold)),
showCheckbox
? CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
translate("Do this for all conflicts"),
),
value: removeCheckboxRemember,
onChanged: (v) {
if (v == null) return;
setState(() => removeCheckboxRemember = v);
},
)
: SizedBox.shrink()
]),
actions: [
TextButton(
style: flatButtonStyle,
onPressed: () => close(false),
child: Text(translate("Cancel"))),
TextButton(
style: flatButtonStyle,
onPressed: () => close(true),
child: Text(translate("OK"))),
]),
useAnimation: false);
(setState, Function(bool v) close) {
cancel() => close(false);
submit() => close(true);
return CustomAlertDialog(
title: Row(
children: [
const Icon(Icons.warning, color: Colors.red),
const SizedBox(width: 20),
Text(title)
],
),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(content),
const SizedBox(height: 5),
Text(translate("This is irreversible!"),
style: const TextStyle(fontWeight: FontWeight.bold)),
showCheckbox
? CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
translate("Do this for all conflicts"),
),
value: removeCheckboxRemember,
onChanged: (v) {
if (v == null) return;
setState(() => removeCheckboxRemember = v);
},
)
: const SizedBox.shrink()
]),
actions: [
TextButton(
style: flatButtonStyle,
onPressed: cancel,
child: Text(translate("Cancel"))),
TextButton(
style: flatButtonStyle,
onPressed: submit,
child: Text(translate("OK"))),
],
onSubmit: submit,
onCancel: cancel,
);
}, useAnimation: false);
}
bool fileConfirmCheckboxRemember = false;
@@ -610,55 +616,59 @@ class FileModel extends ChangeNotifier {
String title, String content, bool showCheckbox) async {
fileConfirmCheckboxRemember = false;
return await parent.target?.dialogManager.show<bool?>(
(setState, Function(bool? v) close) => CustomAlertDialog(
title: Row(
children: [
Icon(Icons.warning, color: Colors.red),
SizedBox(width: 20),
Text(title)
],
),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
translate(
"This file exists, skip or overwrite this file?"),
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 5),
Text(content),
showCheckbox
? CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
translate("Do this for all conflicts"),
),
value: fileConfirmCheckboxRemember,
onChanged: (v) {
if (v == null) return;
setState(() => fileConfirmCheckboxRemember = v);
},
)
: SizedBox.shrink()
]),
actions: [
TextButton(
style: flatButtonStyle,
onPressed: () => close(false),
child: Text(translate("Cancel"))),
TextButton(
style: flatButtonStyle,
onPressed: () => close(null),
child: Text(translate("Skip"))),
TextButton(
style: flatButtonStyle,
onPressed: () => close(true),
child: Text(translate("OK"))),
]),
useAnimation: false);
(setState, Function(bool? v) close) {
cancel() => close(false);
submit() => close(true);
return CustomAlertDialog(
title: Row(
children: [
const Icon(Icons.warning, color: Colors.red),
const SizedBox(width: 20),
Text(title)
],
),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(translate("This file exists, skip or overwrite this file?"),
style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 5),
Text(content),
showCheckbox
? CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
translate("Do this for all conflicts"),
),
value: fileConfirmCheckboxRemember,
onChanged: (v) {
if (v == null) return;
setState(() => fileConfirmCheckboxRemember = v);
},
)
: const SizedBox.shrink()
]),
actions: [
TextButton(
style: flatButtonStyle,
onPressed: cancel,
child: Text(translate("Cancel"))),
TextButton(
style: flatButtonStyle,
onPressed: () => close(null),
child: Text(translate("Skip"))),
TextButton(
style: flatButtonStyle,
onPressed: submit,
child: Text(translate("OK"))),
],
onSubmit: submit,
onCancel: cancel,
);
}, useAnimation: false);
}
sendRemoveFile(String path, int fileNum, bool isLocal) {

View File

@@ -32,7 +32,7 @@ class FfiModel with ChangeNotifier {
Display _display = Display();
var _inputBlocked = false;
final _permissions = Map<String, bool>();
final _permissions = <String, bool>{};
bool? _secure;
bool? _direct;
bool _touchMode = false;
@@ -71,12 +71,13 @@ class FfiModel with ChangeNotifier {
}
}
void updatePermission(Map<String, dynamic> evt) {
void updatePermission(Map<String, dynamic> evt, String id) {
evt.forEach((k, v) {
if (k == 'name' || k.isEmpty) return;
_permissions[k] = v == 'true';
});
print('$_permissions');
KeyboardEnabledState.find(id).value = _permissions['keyboard'] != false;
debugPrint('$_permissions');
notifyListeners();
}
@@ -146,7 +147,7 @@ class FfiModel with ChangeNotifier {
} else if (name == 'clipboard') {
Clipboard.setData(ClipboardData(text: evt['content']));
} else if (name == 'permission') {
parent.target?.ffiModel.updatePermission(evt);
parent.target?.ffiModel.updatePermission(evt, peerId);
} else if (name == 'chat_client_mode') {
parent.target?.chatModel
.receive(ChatModel.clientModeID, evt['text'] ?? "");
@@ -167,10 +168,8 @@ class FfiModel with ChangeNotifier {
parent.target?.fileModel.loadLastJob(evt);
} else if (name == 'update_folder_files') {
parent.target?.fileModel.updateFolderFiles(evt);
} else if (name == 'try_start_without_auth') {
parent.target?.serverModel.loginRequest(evt);
} else if (name == 'on_client_authorized') {
parent.target?.serverModel.onClientAuthorized(evt);
} else if (name == 'add_connection') {
parent.target?.serverModel.addConnection(evt);
} else if (name == 'on_client_remove') {
parent.target?.serverModel.onClientRemove(evt);
} else if (name == 'update_quality_status') {
@@ -185,7 +184,7 @@ class FfiModel with ChangeNotifier {
/// Bind the event listener to receive events from the Rust core.
void updateEventListener(String peerId) {
final void Function(Map<String, dynamic>) cb = (evt) {
cb(evt) {
var name = evt['name'];
if (name == 'msgbox') {
handleMsgBox(evt, peerId);
@@ -205,7 +204,7 @@ class FfiModel with ChangeNotifier {
} else if (name == 'clipboard') {
Clipboard.setData(ClipboardData(text: evt['content']));
} else if (name == 'permission') {
parent.target?.ffiModel.updatePermission(evt);
parent.target?.ffiModel.updatePermission(evt, peerId);
} else if (name == 'chat_client_mode') {
parent.target?.chatModel
.receive(ChatModel.clientModeID, evt['text'] ?? "");
@@ -226,10 +225,8 @@ class FfiModel with ChangeNotifier {
parent.target?.fileModel.loadLastJob(evt);
} else if (name == 'update_folder_files') {
parent.target?.fileModel.updateFolderFiles(evt);
} else if (name == 'try_start_without_auth') {
parent.target?.serverModel.loginRequest(evt);
} else if (name == 'on_client_authorized') {
parent.target?.serverModel.onClientAuthorized(evt);
} else if (name == 'add_connection') {
parent.target?.serverModel.addConnection(evt);
} else if (name == 'on_client_remove') {
parent.target?.serverModel.onClientRemove(evt);
} else if (name == 'update_quality_status') {
@@ -239,7 +236,8 @@ class FfiModel with ChangeNotifier {
} else if (name == 'update_privacy_mode') {
updatePrivacyMode(evt, peerId);
}
};
}
platformFFI.setEventCallback(cb);
}
@@ -321,15 +319,15 @@ class FfiModel with ChangeNotifier {
if (isPeerAndroid) {
_touchMode = true;
if (parent.target?.ffiModel.permissions['keyboard'] != false) {
Timer(Duration(milliseconds: 100), showMobileActionsOverlay);
Timer(const Duration(milliseconds: 100), showMobileActionsOverlay);
}
} else {
_touchMode =
await bind.sessionGetOption(id: peerId, arg: "touch-mode") != '';
}
if (evt['is_file_transfer'] == "true") {
// TODO is file transfer
if (parent.target != null &&
parent.target!.connType == ConnType.fileTransfer) {
parent.target?.fileModel.onReady();
} else {
_pi.displays = [];
@@ -464,15 +462,20 @@ enum ScrollStyle {
}
class CanvasModel with ChangeNotifier {
// image offset of canvas
double _x = 0;
// image offset of canvas
double _y = 0;
// image scale
double _scale = 1.0;
// the tabbar over the image
double tabBarHeight = 0.0;
// TODO multi canvas model
String id = "";
// scroll offset x percent
double _scrollX = 0.0;
// scroll offset y percent
double _scrollY = 0.0;
double _x = 0;
double _y = 0;
double _scale = 1.0;
double _tabBarHeight = 0.0;
String id = ""; // TODO multi canvas model
ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
WeakReference<FFI> parent;
@@ -492,9 +495,6 @@ class CanvasModel with ChangeNotifier {
double get scrollX => _scrollX;
double get scrollY => _scrollY;
set tabBarHeight(double h) => _tabBarHeight = h;
double get tabBarHeight => _tabBarHeight;
void updateViewStyle() async {
final style = await bind.sessionGetOption(id: id, arg: 'view-style');
if (style == null) {
@@ -548,12 +548,11 @@ class CanvasModel with ChangeNotifier {
Size get size {
final size = MediaQueryData.fromWindow(ui.window).size;
return Size(size.width, size.height - _tabBarHeight);
return Size(size.width, size.height - tabBarHeight);
}
void moveDesktopMouse(double x, double y) {
// On mobile platforms, move the canvas with the cursor.
//if (!isDesktop) {
final dw = getDisplayWidth() * _scale;
final dh = getDisplayHeight() * _scale;
var dxOffset = 0;
@@ -579,8 +578,13 @@ class CanvasModel with ChangeNotifier {
if (dxOffset != 0 || dyOffset != 0) {
notifyListeners();
}
//}
parent.target?.cursorModel.moveLocal(x, y);
// If keyboard is not permitted, do not move cursor when mouse is moving.
if (parent.target != null) {
if (parent.target!.ffiModel.keyboard()) {
parent.target!.cursorModel.moveLocal(x, y);
}
}
}
set scale(v) {
@@ -597,11 +601,8 @@ class CanvasModel with ChangeNotifier {
if (isWebDesktop) {
updateViewStyle();
} else {
final size = MediaQueryData.fromWindow(ui.window).size;
final canvasWidth = size.width;
final canvasHeight = size.height - _tabBarHeight;
_x = (canvasWidth - getDisplayWidth() * _scale) / 2;
_y = (canvasHeight - getDisplayHeight() * _scale) / 2;
_x = (size.width - getDisplayWidth() * _scale) / 2;
_y = (size.height - getDisplayHeight() * _scale) / 2;
}
notifyListeners();
}
@@ -613,7 +614,7 @@ class CanvasModel with ChangeNotifier {
void updateScale(double v) {
if (parent.target?.imageModel.image == null) return;
final offset = parent.target?.cursorModel.offset ?? Offset(0, 0);
final offset = parent.target?.cursorModel.offset ?? const Offset(0, 0);
var r = parent.target?.cursorModel.getVisibleRect() ?? Rect.zero;
final px0 = (offset.dx - r.left) * _scale;
final py0 = (offset.dy - r.top) * _scale;
@@ -640,7 +641,7 @@ class CanvasModel with ChangeNotifier {
class CursorModel with ChangeNotifier {
ui.Image? _image;
final _images = Map<int, Tuple3<ui.Image, double, double>>();
final _images = <int, Tuple3<ui.Image, double, double>>{};
double _x = -10000;
double _y = -10000;
double _hotx = 0;
@@ -807,7 +808,7 @@ class CursorModel with ChangeNotifier {
// my throw exception, because the listener maybe already dispose
notifyListeners();
} catch (e) {
print('notify cursor: $e');
debugPrint('notify cursor: $e');
}
});
}
@@ -915,6 +916,8 @@ extension ToString on MouseButtons {
}
}
enum ConnType { defaultConn, fileTransfer, portForward, rdp }
/// FFI class for communicating with the Rust core.
class FFI {
var id = "";
@@ -923,6 +926,7 @@ class FFI {
var alt = false;
var command = false;
var version = "";
var connType = ConnType.defaultConn;
/// dialogManager use late to ensure init after main page binding [globalKey]
late final dialogManager = OverlayDialogManager();
@@ -1076,9 +1080,11 @@ class FFI {
double tabBarHeight = 0.0}) {
assert(!(isFileTransfer && isPortForward), "more than one connect type");
if (isFileTransfer) {
id = 'ft_${id}';
connType = ConnType.fileTransfer;
id = 'ft_$id';
} else if (isPortForward) {
id = 'pf_${id}';
connType = ConnType.portForward;
id = 'pf_$id';
} else {
chatModel.resetClientMode();
canvasModel.id = id;
@@ -1107,7 +1113,7 @@ class FFI {
// every instance will bind a stream
this.id = id;
if (isFileTransfer) {
this.fileModel.initFileFetcher();
fileModel.initFileFetcher();
}
}

View File

@@ -7,6 +7,7 @@ import 'package:flutter_hbb/models/platform_model.dart';
import 'package:wakelock/wakelock.dart';
import '../common.dart';
import '../common/formatter/id_formatter.dart';
import '../desktop/pages/server_page.dart' as Desktop;
import '../desktop/widgets/tabbar_widget.dart';
import '../mobile/pages/server_page.dart';
@@ -29,10 +30,10 @@ class ServerModel with ChangeNotifier {
String _temporaryPasswordLength = "";
late String _emptyIdShow;
late final TextEditingController _serverId;
late final IDTextEditingController _serverId;
final _serverPasswd = TextEditingController(text: "");
final tabController = DesktopTabController();
final tabController = DesktopTabController(tabType: DesktopTabType.cm);
List<Client> _clients = [];
@@ -88,7 +89,7 @@ class ServerModel with ChangeNotifier {
ServerModel(this.parent) {
_emptyIdShow = translate("Generating ...");
_serverId = TextEditingController(text: this._emptyIdShow);
_serverId = IDTextEditingController(text: _emptyIdShow);
Timer.periodic(Duration(seconds: 1), (timer) async {
var status = await bind.mainGetOnlineStatue();
@@ -99,7 +100,7 @@ class ServerModel with ChangeNotifier {
_connectStatus = status;
notifyListeners();
}
final res = await bind.mainCheckClientsLength(length: _clients.length);
final res = await bind.cmCheckClientsLength(length: _clients.length);
if (res != null) {
debugPrint("clients not match!");
updateClientState(res);
@@ -208,46 +209,48 @@ class ServerModel with ChangeNotifier {
/// Toggle the screen sharing service.
toggleService() async {
if (_isStart) {
final res = await parent.target?.dialogManager
.show<bool>((setState, close) => CustomAlertDialog(
title: Row(children: [
Icon(Icons.warning_amber_sharp,
color: Colors.redAccent, size: 28),
SizedBox(width: 10),
Text(translate("Warning")),
]),
content: Text(translate("android_stop_service_tip")),
actions: [
TextButton(
onPressed: () => close(),
child: Text(translate("Cancel"))),
ElevatedButton(
onPressed: () => close(true),
child: Text(translate("OK"))),
],
));
final res =
await parent.target?.dialogManager.show<bool>((setState, close) {
submit() => close(true);
return CustomAlertDialog(
title: Row(children: [
const Icon(Icons.warning_amber_sharp,
color: Colors.redAccent, size: 28),
const SizedBox(width: 10),
Text(translate("Warning")),
]),
content: Text(translate("android_stop_service_tip")),
actions: [
TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton(onPressed: submit, child: Text(translate("OK"))),
],
onSubmit: submit,
onCancel: close,
);
});
if (res == true) {
stopService();
}
} else {
final res = await parent.target?.dialogManager
.show<bool>((setState, close) => CustomAlertDialog(
title: Row(children: [
Icon(Icons.warning_amber_sharp,
color: Colors.redAccent, size: 28),
SizedBox(width: 10),
Text(translate("Warning")),
]),
content: Text(translate("android_service_will_start_tip")),
actions: [
TextButton(
onPressed: () => close(),
child: Text(translate("Cancel"))),
ElevatedButton(
onPressed: () => close(true),
child: Text(translate("OK"))),
],
));
final res =
await parent.target?.dialogManager.show<bool>((setState, close) {
submit() => close(true);
return CustomAlertDialog(
title: Row(children: [
const Icon(Icons.warning_amber_sharp,
color: Colors.redAccent, size: 28),
const SizedBox(width: 10),
Text(translate("Warning")),
]),
content: Text(translate("android_service_will_start_tip")),
actions: [
TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton(onPressed: submit, child: Text(translate("OK"))),
],
onSubmit: submit,
onCancel: close,
);
});
if (res == true) {
startService();
}
@@ -300,7 +303,7 @@ class ServerModel with ChangeNotifier {
}
_fetchID() async {
final old = _serverId.text;
final old = _serverId.id;
var count = 0;
const maxCount = 10;
while (count < maxCount) {
@@ -309,12 +312,12 @@ class ServerModel with ChangeNotifier {
if (id.isEmpty) {
continue;
} else {
_serverId.text = id;
_serverId.id = id;
}
debugPrint("fetch id again at $count:id:${_serverId.text}");
debugPrint("fetch id again at $count:id:${_serverId.id}");
count++;
if (_serverId.text != old) {
if (_serverId.id != old) {
break;
}
}
@@ -344,23 +347,21 @@ class ServerModel with ChangeNotifier {
// force
updateClientState([String? json]) async {
var res = await bind.mainGetClientsState();
var res = await bind.cmGetClientsState();
try {
final List clientsJson = jsonDecode(res);
if (isDesktop && clientsJson.isEmpty && _clients.isNotEmpty) {
// exit cm when >1 peers to no peers
exit(0);
}
_clients.clear();
tabController.state.value.tabs.clear();
for (var clientJson in clientsJson) {
final client = Client.fromJson(clientJson);
_clients.add(client);
tabController.add(TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)));
tabController.add(
TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)),
authorized: client.authorized);
}
notifyListeners();
} catch (e) {
@@ -368,70 +369,89 @@ class ServerModel with ChangeNotifier {
}
}
void loginRequest(Map<String, dynamic> evt) {
void addConnection(Map<String, dynamic> evt) {
try {
final client = Client.fromJson(jsonDecode(evt["client"]));
if (_clients.any((c) => c.id == client.id)) {
return;
if (client.authorized) {
parent.target?.dialogManager.dismissByTag(getLoginDialogTag(client.id));
final index = _clients.indexWhere((c) => c.id == client.id);
if (index < 0) {
_clients.add(client);
} else {
_clients[index].authorized = true;
}
tabController.add(
TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)),
authorized: true);
scrollToBottom();
notifyListeners();
} else {
if (_clients.any((c) => c.id == client.id)) {
return;
}
_clients.add(client);
tabController.add(TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)));
scrollToBottom();
notifyListeners();
if (isAndroid) showLoginDialog(client);
}
_clients.add(client);
tabController.add(TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)));
scrollToBottom();
notifyListeners();
if (isAndroid) showLoginDialog(client);
} catch (e) {
debugPrint("Failed to call loginRequest,error:$e");
}
}
void showLoginDialog(Client client) {
parent.target?.dialogManager.show(
(setState, close) => CustomAlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(translate(client.isFileTransfer
? "File Connection"
: "Screen Connection")),
IconButton(
onPressed: () {
close();
},
icon: Icon(Icons.close))
]),
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate("Do you accept?")),
clientInfo(client),
Text(
translate("android_new_connection_tip"),
style: TextStyle(color: Colors.black54),
),
],
),
actions: [
TextButton(
child: Text(translate("Dismiss")),
onPressed: () {
sendLoginResponse(client, false);
close();
}),
ElevatedButton(
child: Text(translate("Accept")),
onPressed: () {
sendLoginResponse(client, true);
close();
}),
],
parent.target?.dialogManager.show((setState, close) {
cancel() {
sendLoginResponse(client, false);
close();
}
submit() {
sendLoginResponse(client, true);
close();
}
return CustomAlertDialog(
title:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(translate(
client.isFileTransfer ? "File Connection" : "Screen Connection")),
IconButton(
onPressed: () {
close();
},
icon: const Icon(Icons.close))
]),
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate("Do you accept?")),
clientInfo(client),
Text(
translate("android_new_connection_tip"),
style: const TextStyle(color: Colors.black54),
),
tag: getLoginDialogTag(client.id));
],
),
actions: [
TextButton(onPressed: cancel, child: Text(translate("Dismiss"))),
ElevatedButton(onPressed: submit, child: Text(translate("Accept"))),
],
onSubmit: submit,
onCancel: cancel,
);
}, tag: getLoginDialogTag(client.id));
}
scrollToBottom() {
@@ -471,14 +491,18 @@ class ServerModel with ChangeNotifier {
} else {
_clients[index].authorized = true;
}
tabController.add(TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)));
tabController.add(
TabInfo(
key: client.id.toString(),
label: client.name,
closable: false,
page: Desktop.buildConnectionCard(client)),
authorized: true);
scrollToBottom();
notifyListeners();
} catch (e) {}
} catch (e) {
debugPrint("onClientAuthorized:$e");
}
}
void onClientRemove(Map<String, dynamic> evt) {
@@ -486,8 +510,10 @@ class ServerModel with ChangeNotifier {
final id = int.parse(evt['id'] as String);
if (_clients.any((c) => c.id == id)) {
final index = _clients.indexWhere((client) => client.id == id);
_clients.removeAt(index);
tabController.remove(index);
if (index >= 0) {
_clients.removeAt(index);
tabController.remove(index);
}
parent.target?.dialogManager.dismissByTag(getLoginDialogTag(id));
parent.target?.invokeMethod("cancel_notification", id);
}
@@ -558,24 +584,29 @@ String getLoginDialogTag(int id) {
}
showInputWarnAlert(FFI ffi) {
ffi.dialogManager.show((setState, close) => CustomAlertDialog(
title: Text(translate("How to get Android input permission?")),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(translate("android_input_permission_tip1")),
SizedBox(height: 10),
Text(translate("android_input_permission_tip2")),
],
),
actions: [
TextButton(child: Text(translate("Cancel")), onPressed: close),
ElevatedButton(
child: Text(translate("Open System Setting")),
onPressed: () {
ffi.serverModel.initInput();
close();
}),
ffi.dialogManager.show((setState, close) {
submit() {
ffi.serverModel.initInput();
close();
}
return CustomAlertDialog(
title: Text(translate("How to get Android input permission?")),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(translate("android_input_permission_tip1")),
const SizedBox(height: 10),
Text(translate("android_input_permission_tip2")),
],
));
),
actions: [
TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton(
onPressed: submit, child: Text(translate("Open System Setting"))),
],
onSubmit: submit,
onCancel: close,
);
});
}