mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge master
This commit is contained in:
@@ -32,7 +32,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdkVersion 32
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
@@ -119,9 +119,9 @@ class DialogManager {
|
||||
|
||||
static Future<T?> show<T>(DialogBuilder builder,
|
||||
{bool clickMaskDismiss = false,
|
||||
bool backDismiss = false,
|
||||
String? tag,
|
||||
bool useAnimation = true}) async {
|
||||
bool backDismiss = false,
|
||||
String? tag,
|
||||
bool useAnimation = true}) async {
|
||||
final t;
|
||||
if (tag != null) {
|
||||
t = tag;
|
||||
@@ -146,10 +146,11 @@ class DialogManager {
|
||||
}
|
||||
|
||||
class CustomAlertDialog extends StatelessWidget {
|
||||
CustomAlertDialog({required this.title,
|
||||
required this.content,
|
||||
required this.actions,
|
||||
this.contentPadding});
|
||||
CustomAlertDialog(
|
||||
{required this.title,
|
||||
required this.content,
|
||||
required this.actions,
|
||||
this.contentPadding});
|
||||
|
||||
final Widget title;
|
||||
final Widget content;
|
||||
@@ -162,7 +163,7 @@ class CustomAlertDialog extends StatelessWidget {
|
||||
scrollable: true,
|
||||
title: title,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10),
|
||||
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10),
|
||||
content: content,
|
||||
actions: actions,
|
||||
);
|
||||
@@ -361,7 +362,9 @@ late FFI _globalFFI;
|
||||
FFI get gFFI => _globalFFI;
|
||||
|
||||
Future<void> initGlobalFFI() async {
|
||||
debugPrint("_globalFFI init");
|
||||
_globalFFI = FFI();
|
||||
debugPrint("_globalFFI init end");
|
||||
// after `put`, can also be globally found by Get.find<FFI>();
|
||||
Get.put(_globalFFI, permanent: true);
|
||||
await _globalFFI.ffiModel.init();
|
||||
@@ -369,4 +372,4 @@ Future<void> initGlobalFFI() async {
|
||||
await _globalFFI.bind.mainCheckConnectStatus();
|
||||
// global shared preference
|
||||
await Get.putAsync(() => SharedPreferences.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:dash_chat/dash_chat.dart';
|
||||
import 'package:dash_chat_2/dash_chat_2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
@@ -7,8 +7,6 @@ import 'package:provider/provider.dart';
|
||||
import '../../models/model.dart';
|
||||
import 'home_page.dart';
|
||||
|
||||
ChatPage chatPage = ChatPage();
|
||||
|
||||
class ChatPage extends StatelessWidget implements PageShape {
|
||||
@override
|
||||
final title = translate("Chat");
|
||||
@@ -26,7 +24,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
||||
final id = entry.key;
|
||||
final user = entry.value.chatUser;
|
||||
return PopupMenuItem<int>(
|
||||
child: Text("${user.name} ${user.uid}"),
|
||||
child: Text("${user.firstName} ${user.id}"),
|
||||
value: id,
|
||||
);
|
||||
}).toList();
|
||||
@@ -47,19 +45,24 @@ class ChatPage extends StatelessWidget implements PageShape {
|
||||
return Stack(
|
||||
children: [
|
||||
DashChat(
|
||||
inputContainerStyle: BoxDecoration(color: Colors.white70),
|
||||
sendOnEnter: false,
|
||||
// if true,reload keyboard everytime,need fix
|
||||
onSend: (chatMsg) {
|
||||
chatModel.send(chatMsg);
|
||||
},
|
||||
user: chatModel.me,
|
||||
currentUser: chatModel.me,
|
||||
messages:
|
||||
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
||||
[],
|
||||
// default scrollToBottom has bug https://github.com/fayeed/dash_chat/issues/53
|
||||
scrollToBottom: false,
|
||||
scrollController: chatModel.scroller,
|
||||
messageOptions: MessageOptions(
|
||||
showOtherUsersAvatar: false,
|
||||
showTime: true,
|
||||
messageDecorationBuilder: (_, __, ___) =>
|
||||
defaultMessageDecoration(
|
||||
color: MyTheme.accent80,
|
||||
borderTopLeft: 8,
|
||||
borderTopRight: 8,
|
||||
borderBottomRight: 8,
|
||||
borderBottomLeft: 8,
|
||||
)),
|
||||
),
|
||||
chatModel.currentID == ChatModel.clientModeID
|
||||
? SizedBox.shrink()
|
||||
@@ -71,7 +74,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
||||
color: MyTheme.accent80),
|
||||
SizedBox(width: 5),
|
||||
Text(
|
||||
"${currentUser.name ?? ""} ${currentUser.uid ?? ""}",
|
||||
"${currentUser.firstName} ${currentUser.id}",
|
||||
style: TextStyle(color: MyTheme.accent50),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -29,6 +29,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
gFFI.connect(widget.id, isFileTransfer: true);
|
||||
showLoading(translate('Connecting...'));
|
||||
gFFI.ffiModel.updateEventListener(widget.id);
|
||||
Wakelock.enable();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ abstract class PageShape extends Widget {
|
||||
final List<Widget> appBarActions = [];
|
||||
}
|
||||
|
||||
final homeKey = GlobalKey<_HomePageState>();
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
HomePage({Key? key}) : super(key: key);
|
||||
|
||||
@@ -23,12 +25,23 @@ class _HomePageState extends State<HomePage> {
|
||||
var _selectedIndex = 0;
|
||||
final List<PageShape> _pages = [];
|
||||
|
||||
void refreshPages() {
|
||||
setState(() {
|
||||
initPages();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initPages();
|
||||
}
|
||||
|
||||
void initPages() {
|
||||
_pages.clear();
|
||||
_pages.add(ConnectionPage());
|
||||
if (isAndroid) {
|
||||
_pages.addAll([chatPage, ServerPage()]);
|
||||
_pages.addAll([ChatPage(), ServerPage()]);
|
||||
}
|
||||
_pages.add(SettingsPage());
|
||||
}
|
||||
|
||||
@@ -595,6 +595,7 @@ class _RemotePageState extends State<RemotePage> {
|
||||
child: Stack(children: [
|
||||
ImagePaint(),
|
||||
CursorPaint(),
|
||||
QualityMonitor(),
|
||||
getHelpTools(),
|
||||
SizedBox(
|
||||
width: 0,
|
||||
@@ -662,7 +663,7 @@ class _RemotePageState extends State<RemotePage> {
|
||||
more.add(PopupMenuItem<String>(
|
||||
child: Row(
|
||||
children: ([
|
||||
Container(width: 100.0, child: Text(translate('OS Password'))),
|
||||
Text(translate('OS Password')),
|
||||
TextButton(
|
||||
style: flatButtonStyle,
|
||||
onPressed: () {
|
||||
@@ -697,6 +698,13 @@ class _RemotePageState extends State<RemotePage> {
|
||||
value: 'block-input'));
|
||||
}
|
||||
}
|
||||
if (gFFI.ffiModel.permissions["restart"] != false &&
|
||||
(pi.platform == "Linux" ||
|
||||
pi.platform == "Windows" ||
|
||||
pi.platform == "Mac OS")) {
|
||||
more.add(PopupMenuItem<String>(
|
||||
child: Text(translate('Restart Remote Device')), value: 'restart'));
|
||||
}
|
||||
() async {
|
||||
var value = await showMenu(
|
||||
context: context,
|
||||
@@ -730,6 +738,8 @@ class _RemotePageState extends State<RemotePage> {
|
||||
}
|
||||
} else if (value == 'reset_canvas') {
|
||||
gFFI.cursorModel.reset();
|
||||
} else if (value == 'restart') {
|
||||
showRestartRemoteDevice(pi, widget.id);
|
||||
}
|
||||
}();
|
||||
}
|
||||
@@ -952,6 +962,47 @@ class ImagePainter extends CustomPainter {
|
||||
}
|
||||
}
|
||||
|
||||
class QualityMonitor extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => ChangeNotifierProvider.value(
|
||||
value: gFFI.qualityMonitorModel,
|
||||
child: Consumer<QualityMonitorModel>(
|
||||
builder: (context, qualityMonitorModel, child) => Positioned(
|
||||
top: 10,
|
||||
right: 10,
|
||||
child: qualityMonitorModel.show
|
||||
? Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
color: MyTheme.canvasColor.withAlpha(120),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Speed: ${qualityMonitorModel.data.speed}",
|
||||
style: TextStyle(color: MyTheme.grayBg),
|
||||
),
|
||||
Text(
|
||||
"FPS: ${qualityMonitorModel.data.fps}",
|
||||
style: TextStyle(color: MyTheme.grayBg),
|
||||
),
|
||||
Text(
|
||||
"Delay: ${qualityMonitorModel.data.delay} ms",
|
||||
style: TextStyle(color: MyTheme.grayBg),
|
||||
),
|
||||
Text(
|
||||
"Target Bitrate: ${qualityMonitorModel.data.targetBitrate}kb",
|
||||
style: TextStyle(color: MyTheme.grayBg),
|
||||
),
|
||||
Text(
|
||||
"Codec: ${qualityMonitorModel.data.codecFormat}",
|
||||
style: TextStyle(color: MyTheme.grayBg),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: SizedBox.shrink())));
|
||||
}
|
||||
|
||||
CheckboxListTile getToggle(
|
||||
void Function(void Function()) setState, option, name) {
|
||||
return CheckboxListTile(
|
||||
@@ -960,6 +1011,9 @@ CheckboxListTile getToggle(
|
||||
setState(() {
|
||||
gFFI.setByName('toggle_option', option);
|
||||
});
|
||||
if (option == "show-quality-monitor") {
|
||||
gFFI.qualityMonitorModel.checkShowQualityMonitor();
|
||||
}
|
||||
},
|
||||
dense: true,
|
||||
title: Text(translate(name)));
|
||||
@@ -1062,6 +1116,27 @@ void showOptions() {
|
||||
}, clickMaskDismiss: true, backDismiss: true);
|
||||
}
|
||||
|
||||
void showRestartRemoteDevice(PeerInfo pi, String id) async {
|
||||
final res =
|
||||
await DialogManager.show<bool>((setState, close) => CustomAlertDialog(
|
||||
title: Row(children: [
|
||||
Icon(Icons.warning_amber_sharp,
|
||||
color: Colors.redAccent, size: 28),
|
||||
SizedBox(width: 10),
|
||||
Text(translate("Restart Remote Device")),
|
||||
]),
|
||||
content: Text(
|
||||
"${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => close(), child: Text(translate("Cancel"))),
|
||||
ElevatedButton(
|
||||
onPressed: () => close(true), child: Text(translate("OK"))),
|
||||
],
|
||||
));
|
||||
if (res == true) gFFI.setByName('restart_remote_device');
|
||||
}
|
||||
|
||||
void showSetOSPassword(bool login) {
|
||||
final controller = TextEditingController();
|
||||
var password = gFFI.getByName('peer_option', "os-password");
|
||||
|
||||
@@ -200,7 +200,8 @@ class ServerInfo extends StatelessWidget {
|
||||
Icon(Icons.warning_amber_sharp,
|
||||
color: Colors.redAccent, size: 24),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
Expanded(
|
||||
child: Text(
|
||||
translate("Service is not running"),
|
||||
style: TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
@@ -208,7 +209,7 @@ class ServerInfo extends StatelessWidget {
|
||||
fontSize: 18,
|
||||
color: MyTheme.accent80,
|
||||
),
|
||||
)
|
||||
))
|
||||
],
|
||||
)),
|
||||
SizedBox(height: 5),
|
||||
@@ -316,30 +317,35 @@ class PermissionRow extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 140,
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(name,
|
||||
style: TextStyle(fontSize: 16.0, color: MyTheme.accent50))),
|
||||
SizedBox(
|
||||
width: 50,
|
||||
style:
|
||||
TextStyle(fontSize: 16.0, color: MyTheme.accent50)))),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(isOk ? translate("ON") : translate("OFF"),
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: isOk ? Colors.green : Colors.grey)),
|
||||
)
|
||||
],
|
||||
color: isOk ? Colors.green : Colors.grey))),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
translate(isOk ? "CLOSE" : "OPEN"),
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
)),
|
||||
const Divider(height: 0)
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
translate(isOk ? "CLOSE" : "OPEN"),
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
)))),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ class SettingsPage extends StatefulWidget implements PageShape {
|
||||
_SettingsState createState() => _SettingsState();
|
||||
}
|
||||
|
||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
static const url = 'https://rustdesk.com/';
|
||||
final _hasIgnoreBattery = androidVersion >= 26;
|
||||
var _ignoreBatteryOpt = false;
|
||||
const url = 'https://rustdesk.com/';
|
||||
final _hasIgnoreBattery = androidVersion >= 26;
|
||||
var _ignoreBatteryOpt = false;
|
||||
|
||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -147,6 +147,12 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
leading: Icon(Icons.cloud),
|
||||
onPressed: (context) {
|
||||
showServerSettings();
|
||||
}),
|
||||
SettingsTile.navigation(
|
||||
title: Text(translate('Language')),
|
||||
leading: Icon(Icons.translate),
|
||||
onPressed: (context) {
|
||||
showLanguageSettings();
|
||||
})
|
||||
]),
|
||||
SettingsSection(
|
||||
@@ -186,6 +192,42 @@ void showServerSettings() {
|
||||
showServerSettingsWithValue(id, relay, key, api);
|
||||
}
|
||||
|
||||
void showLanguageSettings() {
|
||||
try {
|
||||
final langs = json.decode(gFFI.getByName('langs')) as List<dynamic>;
|
||||
var lang = gFFI.getByName('local_option', 'lang');
|
||||
DialogManager.show((setState, close) {
|
||||
final setLang = (v) {
|
||||
if (lang != v) {
|
||||
setState(() {
|
||||
lang = v;
|
||||
});
|
||||
final msg = Map()
|
||||
..['name'] = 'lang'
|
||||
..['value'] = v;
|
||||
gFFI.setByName('local_option', json.encode(msg));
|
||||
homeKey.currentState?.refreshPages();
|
||||
Future.delayed(Duration(milliseconds: 200), close);
|
||||
}
|
||||
};
|
||||
return CustomAlertDialog(
|
||||
title: SizedBox.shrink(),
|
||||
content: Column(
|
||||
children: [
|
||||
getRadio('Default', '', lang, setLang),
|
||||
Divider(color: MyTheme.border),
|
||||
] +
|
||||
langs.map((e) {
|
||||
final key = e[0] as String;
|
||||
final name = e[1] as String;
|
||||
return getRadio(name, key, lang, setLang);
|
||||
}).toList(),
|
||||
),
|
||||
actions: []);
|
||||
}, backDismiss: true, clickMaskDismiss: true);
|
||||
} catch (_e) {}
|
||||
}
|
||||
|
||||
void showAbout() {
|
||||
DialogManager.show((setState, close) {
|
||||
return CustomAlertDialog(
|
||||
|
||||
@@ -27,7 +27,7 @@ class DraggableChatWindow extends StatelessWidget {
|
||||
height: height,
|
||||
builder: (_, onPanUpdate) {
|
||||
return isIOS
|
||||
? chatPage
|
||||
? ChatPage()
|
||||
: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: CustomAppBar(
|
||||
@@ -68,7 +68,7 @@ class DraggableChatWindow extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
body: chatPage,
|
||||
body: ChatPage(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dash_chat/dash_chat.dart';
|
||||
import 'package:dash_chat_2/dash_chat_2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../mobile/widgets/overlay.dart';
|
||||
@@ -11,8 +11,8 @@ class MessageBody {
|
||||
List<ChatMessage> chatMessages;
|
||||
MessageBody(this.chatUser, this.chatMessages);
|
||||
|
||||
void add(ChatMessage cm) {
|
||||
this.chatMessages.add(cm);
|
||||
void insert(ChatMessage cm) {
|
||||
this.chatMessages.insert(0, cm);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@@ -24,19 +24,15 @@ class ChatModel with ChangeNotifier {
|
||||
static final clientModeID = -1;
|
||||
|
||||
final ChatUser me = ChatUser(
|
||||
uid: "",
|
||||
name: "Me",
|
||||
id: "",
|
||||
firstName: "Me",
|
||||
);
|
||||
|
||||
late final Map<int, MessageBody> _messages = Map()
|
||||
..[clientModeID] = MessageBody(me, []);
|
||||
|
||||
final _scroller = ScrollController();
|
||||
|
||||
var _currentID = clientModeID;
|
||||
|
||||
ScrollController get scroller => _scroller;
|
||||
|
||||
Map<int, MessageBody> get messages => _messages;
|
||||
|
||||
int get currentID => _currentID;
|
||||
@@ -67,8 +63,8 @@ class ChatModel with ChangeNotifier {
|
||||
"Failed to changeCurrentID,remote user doesn't exist");
|
||||
}
|
||||
final chatUser = ChatUser(
|
||||
uid: client.peerId,
|
||||
name: client.name,
|
||||
id: client.peerId,
|
||||
firstName: client.name,
|
||||
);
|
||||
_messages[id] = MessageBody(chatUser, []);
|
||||
_currentID = id;
|
||||
@@ -85,48 +81,39 @@ class ChatModel with ChangeNotifier {
|
||||
late final chatUser;
|
||||
if (id == clientModeID) {
|
||||
chatUser = ChatUser(
|
||||
name: _ffi.target?.ffiModel.pi.username,
|
||||
uid: _ffi.target?.getId(),
|
||||
firstName: _ffi.target?.ffiModel.pi.username,
|
||||
id: _ffi.target?.getId() ?? "",
|
||||
);
|
||||
} else {
|
||||
final client = _ffi.target?.serverModel.clients[id];
|
||||
if (client == null) {
|
||||
return debugPrint("Failed to receive msg,user doesn't exist");
|
||||
}
|
||||
chatUser = ChatUser(uid: client.peerId, name: client.name);
|
||||
chatUser = ChatUser(id: client.peerId, firstName: client.name);
|
||||
}
|
||||
|
||||
if (!_messages.containsKey(id)) {
|
||||
_messages[id] = MessageBody(chatUser, []);
|
||||
}
|
||||
_messages[id]!.add(ChatMessage(text: text, user: chatUser));
|
||||
_messages[id]!.insert(
|
||||
ChatMessage(text: text, user: chatUser, createdAt: DateTime.now()));
|
||||
_currentID = id;
|
||||
notifyListeners();
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
scrollToBottom() {
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
_scroller.animateTo(_scroller.position.maxScrollExtent,
|
||||
duration: Duration(milliseconds: 200),
|
||||
curve: Curves.fastLinearToSlowEaseIn);
|
||||
});
|
||||
}
|
||||
|
||||
send(ChatMessage message) {
|
||||
if (message.text != null && message.text!.isNotEmpty) {
|
||||
_messages[_currentID]?.add(message);
|
||||
if (message.text.isNotEmpty) {
|
||||
_messages[_currentID]?.insert(message);
|
||||
if (_currentID == clientModeID) {
|
||||
_ffi.target?.setByName("chat_client_mode", message.text!);
|
||||
_ffi.target?.setByName("chat_client_mode", message.text);
|
||||
} else {
|
||||
final msg = Map()
|
||||
..["id"] = _currentID
|
||||
..["text"] = message.text!;
|
||||
..["text"] = message.text;
|
||||
_ffi.target?.setByName("chat_server_mode", jsonEncode(msg));
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
close() {
|
||||
|
||||
@@ -318,6 +318,7 @@ class FileModel extends ChangeNotifier {
|
||||
|
||||
onClose() {
|
||||
SmartDialog.dismiss();
|
||||
jobReset();
|
||||
|
||||
// save config
|
||||
Map<String, String> msgMap = Map();
|
||||
|
||||
@@ -84,7 +84,7 @@ class FfiModel with ChangeNotifier {
|
||||
|
||||
void updatePermission(Map<String, dynamic> evt) {
|
||||
evt.forEach((k, v) {
|
||||
if (k == 'name') return;
|
||||
if (k == 'name' || k.isEmpty) return;
|
||||
_permissions[k] = v == 'true';
|
||||
});
|
||||
print('$_permissions');
|
||||
@@ -235,6 +235,8 @@ class FfiModel with ChangeNotifier {
|
||||
parent.target?.serverModel.onClientAuthorized(evt);
|
||||
} else if (name == 'on_client_remove') {
|
||||
parent.target?.serverModel.onClientRemove(evt);
|
||||
} else if (name == 'update_quality_status') {
|
||||
parent.target?.qualityMonitorModel.updateQualityStatus(evt);
|
||||
}
|
||||
};
|
||||
platformFFI.setEventCallback(cb);
|
||||
@@ -267,6 +269,8 @@ class FfiModel with ChangeNotifier {
|
||||
wrongPasswordDialog(id);
|
||||
} else if (type == 'input-password') {
|
||||
enterPasswordDialog(id);
|
||||
} else if (type == 'restarting') {
|
||||
showMsgBox(id, type, title, text, false, hasCancel: false);
|
||||
} else {
|
||||
var hasRetry = evt['hasRetry'] == 'true';
|
||||
showMsgBox(id, type, title, text, hasRetry);
|
||||
@@ -275,8 +279,9 @@ class FfiModel with ChangeNotifier {
|
||||
|
||||
/// Show a message box with [type], [title] and [text].
|
||||
void showMsgBox(
|
||||
String id, String type, String title, String text, bool hasRetry) {
|
||||
msgBox(type, title, text);
|
||||
String id, String type, String title, String text, bool hasRetry,
|
||||
{bool? hasCancel}) {
|
||||
msgBox(type, title, text, hasCancel: hasCancel);
|
||||
_timer?.cancel();
|
||||
if (hasRetry) {
|
||||
_timer = Timer(Duration(seconds: _reconnects), () {
|
||||
@@ -798,6 +803,47 @@ class CursorModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
class QualityMonitorData {
|
||||
String? speed;
|
||||
String? fps;
|
||||
String? delay;
|
||||
String? targetBitrate;
|
||||
String? codecFormat;
|
||||
}
|
||||
|
||||
class QualityMonitorModel with ChangeNotifier {
|
||||
WeakReference<FFI> parent;
|
||||
|
||||
QualityMonitorModel(this.parent);
|
||||
var _show = false;
|
||||
final _data = QualityMonitorData();
|
||||
|
||||
bool get show => _show;
|
||||
QualityMonitorData get data => _data;
|
||||
|
||||
checkShowQualityMonitor() {
|
||||
final show =
|
||||
gFFI.getByName('toggle_option', 'show-quality-monitor') == 'true';
|
||||
if (_show != show) {
|
||||
_show = show;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
updateQualityStatus(Map<String, dynamic> evt) {
|
||||
try {
|
||||
if ((evt["speed"] as String).isNotEmpty) _data.speed = evt["speed"];
|
||||
if ((evt["fps"] as String).isNotEmpty) _data.fps = evt["fps"];
|
||||
if ((evt["delay"] as String).isNotEmpty) _data.delay = evt["delay"];
|
||||
if ((evt["target_bitrate"] as String).isNotEmpty)
|
||||
_data.targetBitrate = evt["target_bitrate"];
|
||||
if ((evt["codec_format"] as String).isNotEmpty)
|
||||
_data.codecFormat = evt["codec_format"];
|
||||
notifyListeners();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mouse button enum.
|
||||
enum MouseButtons { left, right, wheel }
|
||||
|
||||
@@ -831,6 +877,7 @@ class FFI {
|
||||
late final FileModel fileModel;
|
||||
late final AbModel abModel;
|
||||
late final UserModel userModel;
|
||||
late final QualityMonitorModel qualityMonitorModel;
|
||||
|
||||
FFI() {
|
||||
this.imageModel = ImageModel(WeakReference(this));
|
||||
@@ -842,6 +889,7 @@ class FFI {
|
||||
this.fileModel = FileModel(WeakReference(this));
|
||||
this.abModel = AbModel(WeakReference(this));
|
||||
this.userModel = UserModel(WeakReference(this));
|
||||
this.qualityMonitorModel = QualityMonitorModel(WeakReference(this));
|
||||
}
|
||||
|
||||
static FFI newFFI() {
|
||||
|
||||
@@ -113,6 +113,27 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "8.4.0"
|
||||
cached_network_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -147,7 +168,7 @@ packages:
|
||||
name: code_builder
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.2.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -183,6 +204,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.17.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -197,13 +225,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
dash_chat:
|
||||
dash_chat_2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dash_chat
|
||||
name: dash_chat_2
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.16"
|
||||
version: "0.0.12"
|
||||
desktop_multi_window:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -351,6 +379,13 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_blurhash:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_blurhash
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
flutter_breadcrumb:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -358,6 +393,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -394,7 +436,7 @@ packages:
|
||||
name: flutter_smart_dialog
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.5.3+7"
|
||||
version: "4.5.3+8"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -447,13 +489,20 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.15.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.13.4"
|
||||
version: "0.13.5"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -509,7 +558,7 @@ packages:
|
||||
name: image_picker_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.1"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -587,6 +636,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: octo_image
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -656,14 +712,14 @@ packages:
|
||||
name: path_provider_android
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.16"
|
||||
version: "2.0.17"
|
||||
path_provider_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_ios
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
version: "2.0.11"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -740,7 +796,7 @@ packages:
|
||||
name: provider
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "6.0.3"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -758,12 +814,10 @@ packages:
|
||||
qr_code_scanner:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: fix_break_changes_platform
|
||||
resolved-ref: "0feca6f15042c279ff575c559a3430df917b623d"
|
||||
url: "https://github.com/Heap-Hop/qr_code_scanner.git"
|
||||
source: git
|
||||
version: "0.7.0"
|
||||
name: qr_code_scanner
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -771,6 +825,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.27.5"
|
||||
screen_retriever:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -847,7 +908,7 @@ packages:
|
||||
name: shelf
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.2"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -881,6 +942,20 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.2"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.1+1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -909,6 +984,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.0+2"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -937,13 +1019,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
transparent_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: transparent_image
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
tray_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1035,6 +1110,41 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
video_player:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.5"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_android
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.8"
|
||||
video_player_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_avfoundation
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
video_player_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.1.3"
|
||||
video_player_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.12"
|
||||
visibility_detector:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -31,7 +31,7 @@ dependencies:
|
||||
ffi: ^1.1.2
|
||||
path_provider: ^2.0.2
|
||||
external_path: ^1.0.1
|
||||
provider: ^5.0.0
|
||||
provider: ^6.0.3
|
||||
tuple: ^2.0.0
|
||||
wakelock: ^0.5.2
|
||||
device_info_plus: ^3.2.3
|
||||
@@ -40,15 +40,12 @@ dependencies:
|
||||
url_launcher: ^6.0.9
|
||||
shared_preferences: ^2.0.6
|
||||
toggle_switch: ^1.4.0
|
||||
dash_chat: ^1.1.16
|
||||
dash_chat_2: ^0.0.12
|
||||
draggable_float_widget: ^0.0.2
|
||||
settings_ui: ^2.0.2
|
||||
flutter_breadcrumb: ^1.0.1
|
||||
http: ^0.13.4
|
||||
qr_code_scanner:
|
||||
git:
|
||||
url: https://github.com/Heap-Hop/qr_code_scanner.git
|
||||
ref: fix_break_changes_platform
|
||||
qr_code_scanner: ^1.0.0
|
||||
zxing2: ^0.1.0
|
||||
image_picker: ^0.8.5
|
||||
image: ^3.1.3
|
||||
|
||||
Reference in New Issue
Block a user