update android chat,server page

This commit is contained in:
csf 2022-03-22 21:47:42 +08:00
parent 6ce7018f07
commit 1daaa3a4cd
6 changed files with 237 additions and 126 deletions

View File

@ -16,9 +16,8 @@ class InputService : AccessibilityService() {
companion object{ companion object{
var ctx:InputService? = null var ctx:InputService? = null
fun isOpen():Boolean{ val isOpen: Boolean
return ctx!=null get() = ctx!=null
}
} }
private val logTag = "input service" private val logTag = "input service"
private var leftIsDown = false private var leftIsDown = false

View File

@ -12,7 +12,6 @@ import android.provider.Settings
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationManagerCompat
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
@ -77,7 +76,7 @@ class MainActivity : FlutterActivity() {
"check_service" -> { "check_service" -> {
flutterMethodChannel.invokeMethod( flutterMethodChannel.invokeMethod(
"on_permission_changed", "on_permission_changed",
mapOf("name" to "input", "value" to InputService.isOpen().toString()) mapOf("name" to "input", "value" to InputService.isOpen.toString())
) )
flutterMethodChannel.invokeMethod( flutterMethodChannel.invokeMethod(
"on_permission_changed", "on_permission_changed",
@ -88,6 +87,16 @@ class MainActivity : FlutterActivity() {
initInput() initInput()
result.success(true) result.success(true)
} }
"stop_input" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
InputService.ctx?.disableSelf()
}
InputService.ctx = null
flutterMethodChannel.invokeMethod(
"on_permission_changed",
mapOf("name" to "input", "value" to InputService.isOpen.toString())
)
}
else -> {} else -> {}
} }
} }
@ -133,7 +142,7 @@ class MainActivity : FlutterActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val inputPer = InputService.isOpen() val inputPer = InputService.isOpen
Log.d(logTag, "onResume inputPer:$inputPer") Log.d(logTag, "onResume inputPer:$inputPer")
activity.runOnUiThread { activity.runOnUiThread {
flutterMethodChannel.invokeMethod( flutterMethodChannel.invokeMethod(

View File

@ -143,8 +143,8 @@ class FfiModel with ChangeNotifier {
FFI.fileModel.jobError(evt); FFI.fileModel.jobError(evt);
} else if (name == 'try_start_without_auth') { } else if (name == 'try_start_without_auth') {
FFI.serverModel.loginRequest(evt); FFI.serverModel.loginRequest(evt);
} else if (name == 'on_client_logon') { } else if (name == 'on_client_authorized') {
FFI.serverModel.onClientAuthorized(evt);
} else if (name == 'on_client_remove') { } else if (name == 'on_client_remove') {
FFI.serverModel.onClientRemove(evt); FFI.serverModel.onClientRemove(evt);
} }

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/native_model.dart';
import '../common.dart'; import '../common.dart';
import '../pages/server_page.dart'; import '../pages/server_page.dart';
import 'model.dart'; import 'model.dart';
@ -12,6 +13,7 @@ class ServerModel with ChangeNotifier {
bool _isStart = false; bool _isStart = false;
bool _mediaOk = false; bool _mediaOk = false;
bool _inputOk = false; bool _inputOk = false;
late bool _audioOk;
late bool _fileOk; late bool _fileOk;
final _serverId = TextEditingController(text: _emptyIdShow); final _serverId = TextEditingController(text: _emptyIdShow);
final _serverPasswd = TextEditingController(text: ""); final _serverPasswd = TextEditingController(text: "");
@ -24,6 +26,8 @@ class ServerModel with ChangeNotifier {
bool get inputOk => _inputOk; bool get inputOk => _inputOk;
bool get audioOk => _audioOk;
bool get fileOk => _fileOk; bool get fileOk => _fileOk;
TextEditingController get serverId => _serverId; TextEditingController get serverId => _serverId;
@ -35,35 +39,72 @@ class ServerModel with ChangeNotifier {
ServerModel() { ServerModel() {
()async{ ()async{
await Future.delayed(Duration(seconds: 2)); await Future.delayed(Duration(seconds: 2));
final file = FFI.getByName('option', 'enable-file-transfer'); final audioOption = FFI.getByName('option', 'enable-audio');
debugPrint("got file in option:$file"); _audioOk = audioOption.isEmpty; // audio true by default
if (file.isEmpty) {
_fileOk = false;
Map<String, String> res = Map()
..["name"] = "enable-file-transfer"
..["value"] = "N";
FFI.setByName('option', jsonEncode(res)); //
} else {
if (file == "Y") {
_fileOk = true;
} else {
_fileOk = false;
}
}
}();
final fileOption = FFI.getByName('option', 'enable-file-transfer');
_fileOk = fileOption.isEmpty;
Map<String, String> res = Map()
..["name"] = "enable-keyboard"
..["value"] = 'N';
FFI.setByName('option', jsonEncode(res)); // input false by default
}();
}
toggleAudio(){
_audioOk = !_audioOk;
Map<String, String> res = Map()
..["name"] = "enable-audio"
..["value"] = _audioOk ? '' : 'N';
FFI.setByName('option', jsonEncode(res));
notifyListeners();
} }
toggleFile() { toggleFile() {
_fileOk = !_fileOk; _fileOk = !_fileOk;
Map<String, String> res = Map() Map<String, String> res = Map()
..["name"] = "enable-file-transfer" ..["name"] = "enable-file-transfer"
..["value"] = _fileOk ? 'Y' : 'N'; ..["value"] = _fileOk ? '' : 'N';
debugPrint("save option:$res");
FFI.setByName('option', jsonEncode(res)); FFI.setByName('option', jsonEncode(res));
notifyListeners(); notifyListeners();
} }
toggleInput(){
if(_inputOk){
PlatformFFI.invokeMethod("stop_input");
}else{
showInputWarnAlert();
}
}
toggleService() async {
if(_isStart){
final res = await DialogManager.show<bool>((setState, close) => CustomAlertDialog(
title: Text("是否关闭"),
content: Text("关闭录屏服务将自动关闭所有已连接的控制"),
actions: [
TextButton(onPressed: ()=>close(), child: Text("Cancel")),
ElevatedButton(onPressed: ()=>close(true), child: Text("Ok")),
],
));
if(res == true){
stopService();
}
}else{
final res = await DialogManager.show<bool>((setState, close) => CustomAlertDialog(
title: Text("是否开启录屏服务"),
content: Text("将自动开启监听服务"),
actions: [
TextButton(onPressed: ()=>close(), child: Text("Cancel")),
ElevatedButton(onPressed: ()=>close(true), child: Text("Ok")),
],
));
if(res == true){
startService();
}
}
}
Future<Null> startService() async { Future<Null> startService() async {
_isStart = true; _isStart = true;
notifyListeners(); notifyListeners();
@ -78,7 +119,8 @@ class ServerModel with ChangeNotifier {
Future<Null> stopService() async { Future<Null> stopService() async {
_isStart = false; _isStart = false;
release(); _interval?.cancel();
_interval = null;
FFI.serverModel.closeAll(); FFI.serverModel.closeAll();
await FFI.invokeMethod("stop_service"); await FFI.invokeMethod("stop_service");
FFI.setByName("stop_service"); FFI.setByName("stop_service");
@ -121,10 +163,6 @@ class ServerModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
release() {
_interval?.cancel();
_interval = null;
}
changeStatue(String name, bool value) { changeStatue(String name, bool value) {
debugPrint("changeStatue value $value"); debugPrint("changeStatue value $value");
@ -137,8 +175,13 @@ class ServerModel with ChangeNotifier {
} }
break; break;
case "input": case "input":
if(_inputOk!= value){
Map<String, String> res = Map()
..["name"] = "enable-keyboard"
..["value"] = value ? '' : 'N';
FFI.setByName('option', jsonEncode(res));
}
_inputOk = value; _inputOk = value;
//TODO change option
break; break;
default: default:
return; return;
@ -165,7 +208,7 @@ class ServerModel with ChangeNotifier {
final Map<String, dynamic> response = Map(); final Map<String, dynamic> response = Map();
response["id"] = client.id; response["id"] = client.id;
DialogManager.show((setState, close) => CustomAlertDialog( DialogManager.show((setState, close) => CustomAlertDialog(
title: Text("Control Request"), title: Text(client.isFileTransfer?"File":"Screen" + "Control Request"),
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -174,6 +217,7 @@ class ServerModel with ChangeNotifier {
Text(translate("Do you accept?")), Text(translate("Do you accept?")),
SizedBox(height: 20), SizedBox(height: 20),
clientInfo(client), clientInfo(client),
Text(translate("It will be control your device!")),
], ],
), ),
actions: [ actions: [
@ -198,13 +242,20 @@ class ServerModel with ChangeNotifier {
notifyListeners(); notifyListeners();
close(); close();
}), }),
])); ],onWillPop: ()async=>true,),barrierDismissible: true);
} catch (e) { } catch (e) {
debugPrint("loginRequest failed,error:$e"); debugPrint("loginRequest failed,error:$e");
} }
} }
void onClientLogin(Map<String, dynamic> evt) {} void onClientAuthorized(Map<String, dynamic> evt) {
try{
_clients.add(Client.fromJson(jsonDecode(evt['client'])));
notifyListeners();
}catch(e){
}
}
void onClientRemove(Map<String, dynamic> evt) { void onClientRemove(Map<String, dynamic> evt) {
try { try {
@ -213,6 +264,8 @@ class ServerModel with ChangeNotifier {
_clients.remove(client); _clients.remove(client);
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {
// singleWhere fail ,reset the login dialog
DialogManager.reset();
debugPrint("onClientRemove failed,error:$e"); debugPrint("onClientRemove failed,error:$e");
} }
} }
@ -226,13 +279,16 @@ class ServerModel with ChangeNotifier {
} }
class Client { class Client {
int id = 0; // for client connections inner count id int id = 0; // client connections inner count id
bool authorized = false; bool authorized = false;
bool isFileTransfer = false; bool isFileTransfer = false;
String name = ""; String name = "";
String peerId = ""; // for peer user's id,show at app String peerId = ""; // peer user's id,show at app
bool keyboard = false;
bool clipboard = false;
bool audio = false;
Client(this.authorized, this.isFileTransfer, this.name, this.peerId); Client(this.authorized, this.isFileTransfer, this.name, this.peerId,this.keyboard,this.clipboard,this.audio);
Client.fromJson(Map<String, dynamic> json) { Client.fromJson(Map<String, dynamic> json) {
id = json['id']; id = json['id'];
@ -240,6 +296,9 @@ class Client {
isFileTransfer = json['is_file_transfer']; isFileTransfer = json['is_file_transfer'];
name = json['name']; name = json['name'];
peerId = json['peer_id']; peerId = json['peer_id'];
keyboard= json['keyboard'];
clipboard= json['clipboard'];
audio= json['audio'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -249,6 +308,46 @@ class Client {
data['is_file_transfer'] = this.isFileTransfer; data['is_file_transfer'] = this.isFileTransfer;
data['name'] = this.name; data['name'] = this.name;
data['peer_id'] = this.peerId; data['peer_id'] = this.peerId;
data['keyboard'] = this.keyboard;
data['clipboard'] = this.clipboard;
data['audio'] = this.audio;
return data; return data;
} }
} }
showInputWarnAlert() async {
if (globalKey.currentContext == null) return;
DialogManager.reset();
await showDialog<bool>(
context: globalKey.currentContext!,
builder: (alertContext) {
// TODO t
DialogManager.register(alertContext);
return AlertDialog(
title: Text("获取输入权限引导"),
content: Text.rich(TextSpan(style: TextStyle(), children: [
TextSpan(text: "请在接下来的系统设置页\n进入"),
TextSpan(text: " [服务] ", style: TextStyle(color: MyTheme.accent)),
TextSpan(text: "配置页面\n"),
TextSpan(
text: " [RustDesk Input] ",
style: TextStyle(color: MyTheme.accent)),
TextSpan(text: "服务开启")
])),
actions: [
TextButton(
child: Text(translate("Do nothing")),
onPressed: () {
DialogManager.reset();
}),
ElevatedButton(
child: Text(translate("Go System Setting")),
onPressed: () {
FFI.serverModel.initInput();
DialogManager.reset();
}),
],
);
});
DialogManager.drop();
}

View File

@ -4,9 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/chat_model.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../main.dart';
import '../models/model.dart';
import '../models/native_model.dart';
import 'home_page.dart'; import 'home_page.dart';
OverlayEntry? iconOverlayEntry; OverlayEntry? iconOverlayEntry;
@ -15,7 +12,6 @@ OverlayEntry? windowOverlayEntry;
ChatPage chatPage = ChatPage(); ChatPage chatPage = ChatPage();
class ChatPage extends StatelessWidget implements PageShape { class ChatPage extends StatelessWidget implements PageShape {
@override @override
final title = "Chat"; final title = "Chat";
@ -28,17 +24,18 @@ class ChatPage extends StatelessWidget implements PageShape {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
color: MyTheme.grayBg, color: MyTheme.grayBg,
child: Consumer<ChatModel>(builder: (context, chatModel, child) { child: Consumer<ChatModel>(builder: (context, chatModel, child) {
return DashChat( return DashChat(
sendOnEnter: false, // if true,reload keyboard everytime,need fix inputContainerStyle: BoxDecoration(color: Colors.white70),
onSend: (chatMsg) { sendOnEnter: false, // if true,reload keyboard everytime,need fix
chatModel.send(chatMsg); onSend: (chatMsg) {
}, chatModel.send(chatMsg);
user: chatModel.me, },
messages: chatModel.messages[chatModel.currentID], user: chatModel.me,
); messages: chatModel.messages[chatModel.currentID],
})); );
}));
} }
} }
@ -116,14 +113,14 @@ toggleChatOverlay() {
class ChatWindowOverlay extends StatefulWidget { class ChatWindowOverlay extends StatefulWidget {
final double windowWidth = 250; final double windowWidth = 250;
final double windowHeight = 300; final double windowHeight = 350;
@override @override
State<StatefulWidget> createState() => _ChatWindowOverlayState(); State<StatefulWidget> createState() => _ChatWindowOverlayState();
} }
class _ChatWindowOverlayState extends State<ChatWindowOverlay> { class _ChatWindowOverlayState extends State<ChatWindowOverlay> {
Offset _o = Offset(20, 20); Offset _o = Offset(20, 80);
bool _keyboardVisible = false; bool _keyboardVisible = false;
double _saveHeight = 0; double _saveHeight = 0;
double _lastBottomHeight = 0; double _lastBottomHeight = 0;
@ -154,48 +151,47 @@ class _ChatWindowOverlayState extends State<ChatWindowOverlay> {
}); });
} }
checkScreenSize(){ checkScreenSize() {
// TODO // TODO
} }
checkKeyBoard(){ checkKeyboard() {
final bottomHeight = MediaQuery.of(context).viewInsets.bottom; final bottomHeight = MediaQuery.of(context).viewInsets.bottom;
final currentVisible = bottomHeight != 0; final currentVisible = bottomHeight != 0;
debugPrint(bottomHeight.toString() + currentVisible.toString()); debugPrint(bottomHeight.toString() + currentVisible.toString());
// save // save
if (!_keyboardVisible && currentVisible){ if (!_keyboardVisible && currentVisible) {
_saveHeight = _o.dy; _saveHeight = _o.dy;
debugPrint("on save $_saveHeight"); debugPrint("on save $_saveHeight");
} }
// reset // reset
if (_lastBottomHeight>0 && bottomHeight == 0){ if (_lastBottomHeight > 0 && bottomHeight == 0) {
debugPrint("on reset"); debugPrint("on reset");
_o = Offset(_o.dx,_saveHeight); _o = Offset(_o.dx, _saveHeight);
} }
// onKeyboardVisible // onKeyboardVisible
if (_keyboardVisible && currentVisible){ if (_keyboardVisible && currentVisible) {
final sumHeight = bottomHeight + widget.windowHeight; final sumHeight = bottomHeight + widget.windowHeight;
final contextHeight = MediaQuery.of(context).size.height; final contextHeight = MediaQuery.of(context).size.height;
debugPrint("prepare update sumHeight:$sumHeight,contextHeight:$contextHeight"); debugPrint(
if(sumHeight + _o.dy > contextHeight){ "prepare update sumHeight:$sumHeight,contextHeight:$contextHeight");
if (sumHeight + _o.dy > contextHeight) {
final y = contextHeight - sumHeight; final y = contextHeight - sumHeight;
debugPrint("on update"); debugPrint("on update");
_o = Offset(_o.dx,y); _o = Offset(_o.dx, y);
} }
} }
_keyboardVisible = currentVisible; _keyboardVisible = currentVisible;
_lastBottomHeight = bottomHeight; _lastBottomHeight = bottomHeight;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
checkKeyBoard(); checkKeyboard();
checkScreenSize(); checkScreenSize();
return Positioned( return Positioned(
top: _o.dy, top: _o.dy,
@ -206,8 +202,35 @@ class _ChatWindowOverlayState extends State<ChatWindowOverlay> {
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: CustomAppBar( appBar: CustomAppBar(
onPanUpdate: (d) => changeOffset(d.delta), onPanUpdate: (d) => changeOffset(d.delta),
appBar: AppBar( appBar: Container(
title: Text("Chat")), color: MyTheme.accent50,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(padding: EdgeInsets.symmetric(horizontal: 15),child: Text(
"Chat",
style: TextStyle(
color: Colors.white,
fontFamily: 'WorkSans',
fontWeight: FontWeight.bold,
fontSize: 20),
)),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () {
hideChatWindowOverlay();
}, icon: Icon(Icons.keyboard_arrow_down)),
IconButton(onPressed: () {
hideChatWindowOverlay();
hideChatIconOverlay();
}, icon: Icon(Icons.close))
],
)
],
),
),
), ),
body: chatPage, body: chatPage,
)); ));
@ -216,7 +239,7 @@ class _ChatWindowOverlayState extends State<ChatWindowOverlay> {
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final GestureDragUpdateCallback onPanUpdate; final GestureDragUpdateCallback onPanUpdate;
final AppBar appBar; final Widget appBar;
const CustomAppBar( const CustomAppBar(
{Key? key, required this.onPanUpdate, required this.appBar}) {Key? key, required this.onPanUpdate, required this.appBar})

View File

@ -158,20 +158,20 @@ class _PermissionCheckerState extends State<PermissionChecker> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final serverModel = Provider.of<ServerModel>(context); final serverModel = Provider.of<ServerModel>(context);
final androidVersion = PlatformFFI.androidVersion ?? 0; final androidVersion = PlatformFFI.androidVersion ?? 0;
final hasAudioPermission = androidVersion>=33; final hasAudioPermission = androidVersion>=30;
return PaddingCard( return PaddingCard(
title: translate("Configuration Permissions"), title: translate("Configuration Permissions"),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
PermissionRow(translate("Screen Capture"), serverModel.mediaOk, PermissionRow(translate("Screen Capture"), serverModel.mediaOk,
serverModel.startService), serverModel.toggleService),
PermissionRow(translate("Mouse Control"), serverModel.inputOk, PermissionRow(translate("Mouse Control"), serverModel.inputOk,
showInputWarnAlert), serverModel.toggleInput),
PermissionRow(translate("File Transfer"), serverModel.fileOk, PermissionRow(translate("File Transfer"), serverModel.fileOk,
serverModel.toggleFile), serverModel.toggleFile),
hasAudioPermission?PermissionRow(translate("Audio Capture"), serverModel.inputOk, hasAudioPermission?PermissionRow(translate("Audio Capture"), serverModel.inputOk,
showInputWarnAlert):Text("* 当前安卓版本不支持音频捕获",style: TextStyle(color: MyTheme.darkGray),), serverModel.toggleAudio):Text("* 当前安卓版本不支持音频捕获",style: TextStyle(color: MyTheme.darkGray),),
SizedBox(height: 8), SizedBox(height: 8),
serverModel.mediaOk serverModel.mediaOk
? ElevatedButton.icon( ? ElevatedButton.icon(
@ -235,7 +235,8 @@ class ConnectionManager extends StatelessWidget {
return Column( return Column(
children: serverModel.clients children: serverModel.clients
.map((client) => PaddingCard( .map((client) => PaddingCard(
title: translate("Connection"), title: translate(client.isFileTransfer?"File Connection":"Screen Connection"),
titleIcon: client.isFileTransfer?Icons.folder_outlined:Icons.mobile_screen_share,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -259,9 +260,10 @@ class ConnectionManager extends StatelessWidget {
} }
class PaddingCard extends StatelessWidget { class PaddingCard extends StatelessWidget {
PaddingCard({required this.child, this.title}); PaddingCard({required this.child, this.title,this.titleIcon});
final String? title; final String? title;
final IconData? titleIcon;
final Widget child; final Widget child;
@override @override
@ -272,15 +274,20 @@ class PaddingCard extends StatelessWidget {
0, 0,
Padding( Padding(
padding: EdgeInsets.symmetric(vertical: 5.0), padding: EdgeInsets.symmetric(vertical: 5.0),
child: Text( child: Row(
title!, children: [
style: TextStyle( titleIcon !=null?Padding(padding: EdgeInsets.only(right: 10),child:Icon(titleIcon,color: MyTheme.accent80,size: 30)):SizedBox.shrink(),
fontFamily: 'WorkSans', Text(
fontWeight: FontWeight.bold, title!,
fontSize: 22, style: TextStyle(
color: MyTheme.accent80, fontFamily: 'WorkSans',
), fontWeight: FontWeight.bold,
))); fontSize: 22,
color: MyTheme.accent80,
),
)
],
) ));
} }
return Container( return Container(
width: double.maxFinite, width: double.maxFinite,
@ -306,51 +313,25 @@ Widget clientInfo(Client client) {
CircleAvatar( CircleAvatar(
child: Text(client.name[0]), backgroundColor: MyTheme.border), child: Text(client.name[0]), backgroundColor: MyTheme.border),
SizedBox(width: 12), SizedBox(width: 12),
Text(client.name, style: TextStyle(color: MyTheme.idColor)), Column(
SizedBox(width: 8), crossAxisAlignment: CrossAxisAlignment.start,
Text(client.peerId, style: TextStyle(color: MyTheme.idColor)) mainAxisAlignment: MainAxisAlignment.center
,children: [
Text(client.name, style: TextStyle(color: MyTheme.idColor,fontSize: 20)),
SizedBox(width: 8),
Text(client.peerId, style: TextStyle(color: MyTheme.idColor,fontSize: 10))
])
], ],
), ),
Text("类型:${client.isFileTransfer?"管理文件":"屏幕控制"}" ,style: TextStyle(color: MyTheme.darkGray)) // !client.isFileTransfer?Row(
// children: [
// client.audio?Icon(Icons.volume_up):SizedBox.shrink(),
// client.keyboard?Icon(Icons.mouse):SizedBox.shrink(),
// ],
// ):SizedBox.shrink()
]); ]);
} }
showInputWarnAlert() async {
if (globalKey.currentContext == null) return;
DialogManager.reset();
await showDialog<bool>(
context: globalKey.currentContext!,
builder: (alertContext) {
// TODO t
DialogManager.register(alertContext);
return AlertDialog(
title: Text("获取输入权限引导"),
content: Text.rich(TextSpan(style: TextStyle(), children: [
TextSpan(text: "请在接下来的系统设置页\n进入"),
TextSpan(text: " [服务] ", style: TextStyle(color: MyTheme.accent)),
TextSpan(text: "配置页面\n"),
TextSpan(
text: " [RustDesk Input] ",
style: TextStyle(color: MyTheme.accent)),
TextSpan(text: "服务开启")
])),
actions: [
TextButton(
child: Text(translate("Do nothing")),
onPressed: () {
DialogManager.reset();
}),
ElevatedButton(
child: Text(translate("Go System Setting")),
onPressed: () {
FFI.serverModel.initInput();
DialogManager.reset();
}),
],
);
});
DialogManager.drop();
}
void toAndroidChannelInit() { void toAndroidChannelInit() {
FFI.setMethodCallHandler((method, arguments) { FFI.setMethodCallHandler((method, arguments) {