diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index b33286037..f4b2ec82c 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -46,7 +46,9 @@ class MainActivity : FlutterActivity() { when (call.method) { "init_service" -> { Log.d(logTag, "event from flutter,getPer") - getMediaProjection() + if(mainService?.isReady == false){ + getMediaProjection() + } result.success(true) } "start_capture" -> { diff --git a/lib/main.dart b/lib/main.dart index ccf6705fc..969e051a6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -28,6 +28,8 @@ class App extends StatelessWidget { ChangeNotifierProvider.value(value: FFI.imageModel), ChangeNotifierProvider.value(value: FFI.cursorModel), ChangeNotifierProvider.value(value: FFI.canvasModel), + ChangeNotifierProvider.value(value: FFI.serverModel), + ChangeNotifierProvider.value(value: FFI.chatModel), ChangeNotifierProvider.value(value: FFI.fileModel), ], child: MaterialApp( diff --git a/lib/models/chat_model.dart b/lib/models/chat_model.dart index d70956603..854507114 100644 --- a/lib/models/chat_model.dart +++ b/lib/models/chat_model.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:dash_chat/dash_chat.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/pages/chat_page.dart'; @@ -6,39 +8,65 @@ import 'model.dart'; import 'native_model.dart'; class ChatModel with ChangeNotifier { - final List _messages = []; + // -1作为客户端模式的id,客户端模式下此id唯一 + // 其它正整数的id,来自被控服务器模式下的其他客户端的id,每个客户端有不同的id + // 注意 此id和peer_id不同,服务端模式下的id等同于conn的顺序累加id + static final clientModeID = -1; + + final Map> _messages = Map()..[clientModeID] = []; final ChatUser me = ChatUser( - name:"me", + uid:"", + name: "me", + customProperties: Map()..["id"] = clientModeID ); + var _currentID = clientModeID; + get messages => _messages; - receive(String text){ + get currentID => _currentID; + + receive(int id, String text) { if (text.isEmpty) return; // first message show overlay icon - if (iconOverlayEntry == null){ + if (iconOverlayEntry == null) { showChatIconOverlay(); } - _messages.add(ChatMessage(text: text, user: ChatUser( - name:FFI.ffiModel.pi.username, - uid: FFI.getId(), - ))); + if(!_messages.containsKey(id)){ + _messages[id] = []; + } + // TODO peer info + _messages[id]?.add(ChatMessage( + text: text, + user: ChatUser( + name: FFI.ffiModel.pi.username, + uid: FFI.getId(), + ))); + _currentID = id; notifyListeners(); } - send(ChatMessage message){ - _messages.add(message); - if(message.text != null && message.text!.isNotEmpty){ - PlatformFFI.setByName("chat",message.text!); + send(ChatMessage message) { + if (message.text != null && message.text!.isNotEmpty) { + _messages[_currentID]?.add(message); + if (_currentID == clientModeID) { + PlatformFFI.setByName("chat_client_mode", message.text!); + } else { + final msg = Map() + ..["id"] = _currentID + ..["text"] = message.text!; + PlatformFFI.setByName("chat_server_mode", jsonEncode(msg)); + } } notifyListeners(); } - release(){ + release() { hideChatIconOverlay(); hideChatWindowOverlay(); + _messages.forEach((key, value) => value.clear()); _messages.clear(); notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/models/model.dart b/lib/models/model.dart index 2db0dbaa7..42e20392f 100644 --- a/lib/models/model.dart +++ b/lib/models/model.dart @@ -129,8 +129,10 @@ class FfiModel with ChangeNotifier { Clipboard.setData(ClipboardData(text: evt['content'])); } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); - } else if (name == 'chat') { - FFI.chatModel.receive(evt['text'] ?? ""); + } else if (name == 'chat_client_mode') { + FFI.chatModel.receive(ChatModel.clientModeID,evt['text'] ?? ""); + } else if (name == 'chat_server_mode') { + FFI.chatModel.receive(int.parse(evt['id'] as String),evt['text'] ?? ""); } else if (name == 'file_dir') { FFI.fileModel.receiveFileDir(evt); } else if (name == 'job_progress') { diff --git a/lib/models/native_model.dart b/lib/models/native_model.dart index aca1b1869..342c99df5 100644 --- a/lib/models/native_model.dart +++ b/lib/models/native_model.dart @@ -24,6 +24,7 @@ class PlatformFFI { static Pointer? _lastRgbaFrame; static String _dir = ''; static String _homeDir = ''; + static int? _androidVersion; static F2? _getByName; static F3? _setByName; static F4? _freeRgba; @@ -48,6 +49,8 @@ class PlatformFFI { return packageInfo.version; } + static int? get androidVersion => _androidVersion; + static String getByName(String name, [String arg = '']) { if (_getByName == null) return ''; var a = name.toNativeUtf8(); @@ -94,6 +97,7 @@ class PlatformFFI { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; name = '${androidInfo.brand}-${androidInfo.model}'; id = androidInfo.id.hashCode.toString(); + _androidVersion = androidInfo.version.sdkInt; } else { IosDeviceInfo iosInfo = await deviceInfo.iosInfo; name = iosInfo.utsname.machine; diff --git a/lib/models/server_model.dart b/lib/models/server_model.dart index 13ce9d67b..a0822708a 100644 --- a/lib/models/server_model.dart +++ b/lib/models/server_model.dart @@ -1,29 +1,144 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import '../common.dart'; import '../pages/server_page.dart'; import 'model.dart'; +final _emptyIdShow = translate("connecting_status"); + class ServerModel with ChangeNotifier { + Timer? _interval; + bool _isStart = false; bool _mediaOk = false; bool _inputOk = false; + late bool _fileOk; + final _serverId = TextEditingController(text: _emptyIdShow); + final _serverPasswd = TextEditingController(text: ""); + List _clients = []; + bool get isStart => _isStart; + bool get mediaOk => _mediaOk; bool get inputOk => _inputOk; + bool get fileOk => _fileOk; + + TextEditingController get serverId => _serverId; + + TextEditingController get serverPasswd => _serverPasswd; + List get clients => _clients; - ServerModel(); + ServerModel() { + ()async{ + await Future.delayed(Duration(seconds: 2)); + final file = FFI.getByName('option', 'enable-file-transfer'); + debugPrint("got file in option:$file"); + if (file.isEmpty) { + _fileOk = false; + Map res = Map() + ..["name"] = "enable-file-transfer" + ..["value"] = "N"; + FFI.setByName('option', jsonEncode(res)); // 重新设置默认值 + } else { + if (file == "Y") { + _fileOk = true; + } else { + _fileOk = false; + } + } + }(); + + } + + toggleFile() { + _fileOk = !_fileOk; + Map res = Map() + ..["name"] = "enable-file-transfer" + ..["value"] = _fileOk ? 'Y' : 'N'; + debugPrint("save option:$res"); + FFI.setByName('option', jsonEncode(res)); + notifyListeners(); + } + + Future startService() async { + _isStart = true; + notifyListeners(); + FFI.setByName("ensure_init_event_queue"); + _interval = Timer.periodic(Duration(milliseconds: 30), (timer) { + FFI.ffiModel.update("", (_, __) {}); + }); + await FFI.invokeMethod("init_service"); + FFI.setByName("start_service"); + getIDPasswd(); + } + + Future stopService() async { + _isStart = false; + release(); + FFI.serverModel.closeAll(); + await FFI.invokeMethod("stop_service"); + FFI.setByName("stop_service"); + notifyListeners(); + } + + Future initInput() async { + await FFI.invokeMethod("init_input"); + } + + getIDPasswd() async { + if (_serverId.text != _emptyIdShow && _serverPasswd.text != "") { + return; + } + var count = 0; + const maxCount = 10; + while (count < maxCount) { + await Future.delayed(Duration(seconds: 1)); + final id = FFI.getByName("server_id"); + final passwd = FFI.getByName("server_password"); + if (id.isEmpty) { + continue; + } else { + _serverId.text = id; + } + + if (passwd.isEmpty) { + continue; + } else { + _serverPasswd.text = passwd; + } + + debugPrint( + "fetch id & passwd again at $count:id:${_serverId.text},passwd:${_serverPasswd.text}"); + count++; + if (_serverId.text != _emptyIdShow && _serverPasswd.text.isNotEmpty) { + break; + } + } + notifyListeners(); + } + + release() { + _interval?.cancel(); + _interval = null; + } changeStatue(String name, bool value) { + debugPrint("changeStatue value $value"); switch (name) { case "media": _mediaOk = value; + debugPrint("value $value,_isStart:$_isStart"); + if(value && !_isStart){ + startService(); + } break; case "input": _inputOk = value; + //TODO change option break; default: return; @@ -36,76 +151,77 @@ class ServerModel with ChangeNotifier { debugPrint("getByName clients_state string:$res"); try { final List clientsJson = jsonDecode(res); - _clients = clientsJson.map((clientJson) => Client.fromJson(jsonDecode(res))).toList(); + _clients = clientsJson + .map((clientJson) => Client.fromJson(jsonDecode(res))) + .toList(); debugPrint("updateClientState:${_clients.toString()}"); notifyListeners(); } catch (e) {} } - loginRequest(Map evt){ - try{ + loginRequest(Map evt) { + try { final client = Client.fromJson(jsonDecode(evt["client"])); - final Map response = Map(); + final Map response = Map(); response["id"] = client.id; DialogManager.show((setState, close) => CustomAlertDialog( - title: Text("Control Request"), - content: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(translate("Do you accept?")), - SizedBox(height: 20), - clientInfo(client), - ], - ), - actions: [ - TextButton( - child: Text(translate("Dismiss")), - onPressed: () { - response["res"] = false; - FFI.setByName("login_res", jsonEncode(response)); - close(); - }), - ElevatedButton( - child: Text(translate("Accept")), - onPressed: () async { - response["res"] = true; - FFI.setByName("login_res", jsonEncode(response)); - if (!client.isFileTransfer) { - bool res = await FFI.invokeMethod("start_capture"); // to Android service - debugPrint("_toAndroidStartCapture:$res"); - } - _clients.add(client); - notifyListeners(); - close(); - }), - - ])); - }catch (e){ + title: Text("Control Request"), + content: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(translate("Do you accept?")), + SizedBox(height: 20), + clientInfo(client), + ], + ), + actions: [ + TextButton( + child: Text(translate("Dismiss")), + onPressed: () { + response["res"] = false; + FFI.setByName("login_res", jsonEncode(response)); + close(); + }), + ElevatedButton( + child: Text(translate("Accept")), + onPressed: () async { + response["res"] = true; + FFI.setByName("login_res", jsonEncode(response)); + if (!client.isFileTransfer) { + bool res = await FFI.invokeMethod( + "start_capture"); // to Android service + debugPrint("_toAndroidStartCapture:$res"); + } + _clients.add(client); + notifyListeners(); + close(); + }), + ])); + } catch (e) { debugPrint("loginRequest failed,error:$e"); } } - void onClientLogin(Map evt){ - - } + void onClientLogin(Map evt) {} void onClientRemove(Map evt) { - try{ + try { final id = int.parse(evt['id'] as String); Client client = _clients.singleWhere((c) => c.id == id); _clients.remove(client); notifyListeners(); - }catch(e){ + } catch (e) { debugPrint("onClientRemove failed,error:$e"); } } - closeAll(){ + closeAll() { _clients.forEach((client) { - FFI.setByName("close_conn",client.id.toString()); + FFI.setByName("close_conn", client.id.toString()); }); + _clients = []; } } @@ -136,4 +252,3 @@ class Client { return data; } } - diff --git a/lib/pages/chat_page.dart b/lib/pages/chat_page.dart index 8bbabec4f..46f239167 100644 --- a/lib/pages/chat_page.dart +++ b/lib/pages/chat_page.dart @@ -27,9 +27,7 @@ class ChatPage extends StatelessWidget implements PageShape { @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: FFI.chatModel, - child: Container( + return Container( color: MyTheme.grayBg, child: Consumer(builder: (context, chatModel, child) { return DashChat( @@ -38,9 +36,9 @@ class ChatPage extends StatelessWidget implements PageShape { chatModel.send(chatMsg); }, user: chatModel.me, - messages: chatModel.messages, + messages: chatModel.messages[chatModel.currentID], ); - }))); + })); } } diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 8b88eac27..ec2d5c27c 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -57,6 +57,11 @@ class _HomePageState extends State { selectedItemColor: MyTheme.accent, unselectedItemColor: MyTheme.darkGray, onTap: (index) => setState(() { + // close chat overlay when go chat page + if(index == 1 && _selectedIndex!=index){ + hideChatIconOverlay(); + hideChatWindowOverlay(); + } _selectedIndex = index; }), ), diff --git a/lib/pages/remote_page.dart b/lib/pages/remote_page.dart index 09ae6a4e0..9a7565639 100644 --- a/lib/pages/remote_page.dart +++ b/lib/pages/remote_page.dart @@ -456,7 +456,7 @@ class _RemotePageState extends State { final keyboard = FFI.ffiModel.permissions['keyboard'] != false; var paints = [ImagePaint()]; if (keyboard || - FFI.getByName('toggle-option', 'show-remote-cursor') == 'true') { + FFI.getByName('toggle_option', 'show-remote-cursor') == 'true') { paints.add(CursorPaint()); } return MouseRegion( diff --git a/lib/pages/server_page.dart b/lib/pages/server_page.dart index dfd23da28..162f58bbd 100644 --- a/lib/pages/server_page.dart +++ b/lib/pages/server_page.dart @@ -1,9 +1,10 @@ -import 'dart:async'; +import 'package:device_info/device_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:provider/provider.dart'; import '../common.dart'; +import '../models/native_model.dart'; import '../models/server_model.dart'; import 'home_page.dart'; import '../models/model.dart'; @@ -38,29 +39,26 @@ class ServerPage extends StatelessWidget implements PageShape { @override Widget build(BuildContext context) { checkService(); - return ChangeNotifierProvider.value( - value: FFI.serverModel, - child: SingleChildScrollView( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - ServerInfo(), - PermissionChecker(), - ConnectionManager(), - SizedBox.fromSize(size: Size(0, 15.0)), // Bottom padding - ], - ), - ), - ), - ); + return Consumer( + builder: (context, serverModel, child) => SingleChildScrollView( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ServerInfo(), + PermissionChecker(), + ConnectionManager(), + SizedBox.fromSize(size: Size(0, 15.0)), // Bottom padding + ], + ), + ), + )); } } void checkService() { // 检测当前服务状态,若已存在服务则异步更新数据回来 FFI.invokeMethod("check_service"); // jvm - FFI.serverModel.updateClientState(); } class ServerInfo extends StatefulWidget { @@ -69,101 +67,84 @@ class ServerInfo extends StatefulWidget { } class _ServerInfoState extends State { + final model = FFI.serverModel; var _passwdShow = false; - Timer? _interval; - - // TODO set ID / PASSWORD - var _serverId = TextEditingController(text: ""); - var _serverPasswd = TextEditingController(text: ""); - var _emptyIdShow = translate("connecting_status"); - - @override - void initState() { - super.initState(); - var id = FFI.getByName("server_id"); - - // TODO 需要重新优化开启监听 开启监听服务后再开始pop_event - FFI.setByName("ensure_init_event_queue"); - _interval = Timer.periodic(Duration(milliseconds: 30), - (timer) { - FFI.ffiModel.update("", (_, __) {});}); - - _serverId.text = id == "" ? _emptyIdShow : id; - _serverPasswd.text = FFI.getByName("server_password"); - if (_serverId.text == _emptyIdShow || _serverPasswd.text == "") { - fetchConfigAgain(); - } - } - - @override - void dispose() { - _interval?.cancel(); - super.dispose(); - } @override Widget build(BuildContext context) { - return myCard(Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - readOnly: true, - style: TextStyle( - fontSize: 25.0, - fontWeight: FontWeight.bold, - color: MyTheme.accent), - controller: _serverId, - decoration: InputDecoration( - icon: const Icon(Icons.perm_identity), - labelText: translate("ID"), - labelStyle: - TextStyle(fontWeight: FontWeight.bold, color: MyTheme.accent50), - ), - onSaved: (String? value) {}, - ), - TextFormField( - readOnly: true, - obscureText: !_passwdShow, - style: TextStyle( - fontSize: 25.0, - fontWeight: FontWeight.bold, - color: MyTheme.accent), - controller: _serverPasswd, - decoration: InputDecoration( - icon: const Icon(Icons.lock), - labelText: translate("Password"), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, color: MyTheme.accent50), - suffix: IconButton( - icon: Icon(Icons.visibility), - onPressed: () { - setState(() { - _passwdShow = !_passwdShow; - }); - })), - onSaved: (String? value) {}, - ), - ], - )); - } - - fetchConfigAgain() async { - FFI.setByName("start_service"); - var count = 0; - const maxCount = 10; - while (count < maxCount) { - if (_serverId.text != _emptyIdShow && _serverPasswd.text != "") { - break; - } - await Future.delayed(Duration(seconds: 2)); - var id = FFI.getByName("server_id"); - _serverId.text = id == "" ? _emptyIdShow : id; - _serverPasswd.text = FFI.getByName("server_password"); - debugPrint( - "fetch id & passwd again at $count:id:${_serverId.text},passwd:${_serverPasswd.text}"); - count++; - } - FFI.setByName("stop_service"); + return model.isStart + ? PaddingCard( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + readOnly: true, + style: TextStyle( + fontSize: 25.0, + fontWeight: FontWeight.bold, + color: MyTheme.accent), + controller: model.serverId, + decoration: InputDecoration( + icon: const Icon(Icons.perm_identity), + labelText: translate("ID"), + labelStyle: TextStyle( + fontWeight: FontWeight.bold, color: MyTheme.accent50), + ), + onSaved: (String? value) {}, + ), + TextFormField( + readOnly: true, + obscureText: !_passwdShow, + style: TextStyle( + fontSize: 25.0, + fontWeight: FontWeight.bold, + color: MyTheme.accent), + controller: model.serverPasswd, + decoration: InputDecoration( + icon: const Icon(Icons.lock), + labelText: translate("Password"), + labelStyle: TextStyle( + fontWeight: FontWeight.bold, color: MyTheme.accent50), + suffix: IconButton( + icon: Icon(Icons.visibility), + onPressed: () { + setState(() { + _passwdShow = !_passwdShow; + }); + })), + onSaved: (String? value) {}, + ), + ], + )) + : PaddingCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center( + child: Row( + children: [ + Icon(Icons.warning_amber_sharp, + color: Colors.redAccent, size: 24), + SizedBox(width: 10), + Text( + "屏幕共享尚未开启", + style: TextStyle( + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold, + fontSize: 18, + color: MyTheme.accent80, + ), + ) + ], + )), + SizedBox(height: 5), + Center( + child: Text( + "点击[启动服务]或打开Screen Capture 开启共享手机屏幕", + style: TextStyle(fontSize: 12, color: MyTheme.darkGray), + )) + ], + )); } } @@ -176,30 +157,35 @@ class _PermissionCheckerState extends State { @override Widget build(BuildContext context) { final serverModel = Provider.of(context); - - return myCard(Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - cardTitle(translate("Configuration Permissions")), - PermissionRow( - translate("Media"), serverModel.mediaOk, _toAndroidInitService), - const Divider(height: 0), - PermissionRow( - translate("Input"), serverModel.inputOk, showInputWarnAlert), - const Divider(), - serverModel.mediaOk - ? ElevatedButton.icon( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.red)), - icon: Icon(Icons.stop), - onPressed: _toAndroidStopService, - label: Text(translate("Stop service"))) - : ElevatedButton.icon( - icon: Icon(Icons.play_arrow), - onPressed: _toAndroidInitService, - label: Text(translate("Start Service"))), - ], - )); + final androidVersion = PlatformFFI.androidVersion ?? 0; + final hasAudioPermission = androidVersion>=33; + return PaddingCard( + title: translate("Configuration Permissions"), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PermissionRow(translate("Screen Capture"), serverModel.mediaOk, + serverModel.startService), + PermissionRow(translate("Mouse Control"), serverModel.inputOk, + showInputWarnAlert), + PermissionRow(translate("File Transfer"), serverModel.fileOk, + serverModel.toggleFile), + hasAudioPermission?PermissionRow(translate("Audio Capture"), serverModel.inputOk, + showInputWarnAlert):Text("* 当前安卓版本不支持音频捕获",style: TextStyle(color: MyTheme.darkGray),), + SizedBox(height: 8), + serverModel.mediaOk + ? ElevatedButton.icon( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.red)), + icon: Icon(Icons.stop), + onPressed: serverModel.stopService, + label: Text(translate("Stop service"))) + : ElevatedButton.icon( + icon: Icon(Icons.play_arrow), + onPressed: serverModel.startService, + label: Text(translate("Start Service"))), + ], + )); } } @@ -218,7 +204,7 @@ class PermissionRow extends StatelessWidget { Row( children: [ SizedBox( - width: 70, + width: 140, child: Text(name, style: TextStyle(fontSize: 16.0, color: MyTheme.accent50))), SizedBox( @@ -231,11 +217,12 @@ class PermissionRow extends StatelessWidget { ], ), TextButton( - onPressed: isOk ? null : onPressed, + onPressed: onPressed, child: Text( - translate("OPEN"), + translate(isOk ?"CLOSE":"OPEN"), style: TextStyle(fontWeight: FontWeight.bold), )), + const Divider(height: 0) ], ); } @@ -246,95 +233,86 @@ class ConnectionManager extends StatelessWidget { Widget build(BuildContext context) { final serverModel = Provider.of(context); return Column( - children: serverModel.clients.map((client) => - myCard(Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - cardTitle(translate("Connection")), - Padding( - padding: EdgeInsets.symmetric(vertical: 5.0), - child: clientInfo(client), - ), - ElevatedButton.icon( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.red)), - icon: Icon(Icons.close), - onPressed: () { - FFI.setByName("close_conn",client.id.toString()); - }, - label: Text(translate("Close"))) - ], - )) - ).toList() - ); + children: serverModel.clients + .map((client) => PaddingCard( + title: translate("Connection"), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 5.0), + child: clientInfo(client), + ), + ElevatedButton.icon( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Colors.red)), + icon: Icon(Icons.close), + onPressed: () { + FFI.setByName("close_conn", client.id.toString()); + }, + label: Text(translate("Close"))) + ], + ))) + .toList()); } } -Widget cardTitle(String text) { - return Padding( - padding: EdgeInsets.symmetric(vertical: 5.0), - child: Text( - text, - style: TextStyle( - fontFamily: 'WorkSans', - fontWeight: FontWeight.bold, - fontSize: 22, - color: MyTheme.accent80, - ), - )); -} +class PaddingCard extends StatelessWidget { + PaddingCard({required this.child, this.title}); -Widget myCard(Widget child) { - return Container( - width: double.maxFinite, - child: Card( - margin: EdgeInsets.fromLTRB(15.0, 15.0, 15.0, 0), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0), - child: child, - ), - )); + final String? title; + final Widget child; + + @override + Widget build(BuildContext context) { + final children = [child]; + if (title != null) { + children.insert( + 0, + Padding( + padding: EdgeInsets.symmetric(vertical: 5.0), + child: Text( + title!, + style: TextStyle( + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold, + fontSize: 22, + color: MyTheme.accent80, + ), + ))); + } + return Container( + width: double.maxFinite, + child: Card( + margin: EdgeInsets.fromLTRB(15.0, 15.0, 15.0, 0), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ), + )); + } } Widget clientInfo(Client client) { - return Row( - children: [ - CircleAvatar(child: Text(client.name[0]), backgroundColor: MyTheme.border), - SizedBox(width: 12), - Text(client.name, style: TextStyle(color: MyTheme.idColor)), - SizedBox(width: 8), - Text(client.peerId, style: TextStyle(color: MyTheme.idColor)) - ], - ); -} - -Future _toAndroidInitService() async { - bool res = await FFI.invokeMethod("init_service"); - FFI.setByName("start_service"); - debugPrint("_toAndroidInitService:$res"); -} - -// Future _toAndroidStartCapture() async { -// bool res = await FFI.invokeMethod("start_capture"); -// debugPrint("_toAndroidStartCapture:$res"); -// } - -// Future _toAndroidStopCapture() async { -// bool res = await FFI.invokeMethod("stop_capture"); -// debugPrint("_toAndroidStopCapture:$res"); -// } - -Future _toAndroidStopService() async { - FFI.serverModel.closeAll(); - - bool res = await FFI.invokeMethod("stop_service"); - FFI.setByName("stop_service"); - debugPrint("_toAndroidStopSer:$res"); -} - -Future _toAndroidInitInput() async { - bool res = await FFI.invokeMethod("init_input"); - debugPrint("_toAndroidInitInput:$res"); + return Column( + crossAxisAlignment: CrossAxisAlignment.start + ,children: [ + Row( + children: [ + CircleAvatar( + child: Text(client.name[0]), backgroundColor: MyTheme.border), + SizedBox(width: 12), + Text(client.name, style: TextStyle(color: MyTheme.idColor)), + SizedBox(width: 8), + Text(client.peerId, style: TextStyle(color: MyTheme.idColor)) + ], + ), + Text("类型:${client.isFileTransfer?"管理文件":"屏幕控制"}" ,style: TextStyle(color: MyTheme.darkGray)) + ]); } showInputWarnAlert() async { @@ -347,7 +325,6 @@ showInputWarnAlert() async { DialogManager.register(alertContext); return AlertDialog( title: Text("获取输入权限引导"), - // content: Text("请在接下来的系统设置页面 \n进入 [服务] 配置页面\n将[RustDesk Input]服务开启"), content: Text.rich(TextSpan(style: TextStyle(), children: [ TextSpan(text: "请在接下来的系统设置页\n进入"), TextSpan(text: " [服务] ", style: TextStyle(color: MyTheme.accent)), @@ -366,7 +343,7 @@ showInputWarnAlert() async { ElevatedButton( child: Text(translate("Go System Setting")), onPressed: () { - _toAndroidInitInput(); + FFI.serverModel.initInput(); DialogManager.reset(); }), ], @@ -380,27 +357,12 @@ void toAndroidChannelInit() { debugPrint("flutter got android msg,$method,$arguments"); try { switch (method) { - // case "try_start_without_auth": - // { - // FFI.serverModel.updateClientState(); - // debugPrint( - // "pre show loginAlert:${FFI.serverModel.isFileTransfer.toString()}"); - // showLoginReqAlert(FFI.serverModel.peerID, FFI.serverModel.peerName); - // debugPrint("from jvm:try_start_without_auth done"); - // break; - // } case "start_capture": { DialogManager.reset(); FFI.serverModel.updateClientState(); break; } - // case "stop_capture": - // { - // DialogManager.reset(); - // FFI.serverModel.setPeer(false); - // break; - // } case "on_permission_changed": { var name = arguments["name"] as String;