update android chat,server page
This commit is contained in:
parent
6ce7018f07
commit
1daaa3a4cd
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
@ -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})
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user