mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-29 08:19:05 +08:00
commit
5baed21fce
@ -32,7 +32,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 32
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ const G = M * K;
|
|||||||
|
|
||||||
String readableFileSize(double size) {
|
String readableFileSize(double size) {
|
||||||
if (size < K) {
|
if (size < K) {
|
||||||
return size.toString() + " B";
|
return size.toStringAsFixed(2) + " B";
|
||||||
} else if (size < M) {
|
} else if (size < M) {
|
||||||
return (size / K).toStringAsFixed(2) + " KB";
|
return (size / K).toStringAsFixed(2) + " KB";
|
||||||
} else if (size < G) {
|
} else if (size < G) {
|
||||||
|
@ -39,7 +39,7 @@ class App extends StatelessWidget {
|
|||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
),
|
),
|
||||||
home: !isAndroid ? WebHomePage() : HomePage(),
|
home: !isAndroid ? WebHomePage() : HomePage(key: homeKey),
|
||||||
navigatorObservers: [
|
navigatorObservers: [
|
||||||
FirebaseAnalyticsObserver(analytics: analytics),
|
FirebaseAnalyticsObserver(analytics: analytics),
|
||||||
FlutterSmartDialog.observer
|
FlutterSmartDialog.observer
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:convert';
|
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 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../widgets/overlay.dart';
|
import '../widgets/overlay.dart';
|
||||||
@ -11,8 +11,8 @@ class MessageBody {
|
|||||||
List<ChatMessage> chatMessages;
|
List<ChatMessage> chatMessages;
|
||||||
MessageBody(this.chatUser, this.chatMessages);
|
MessageBody(this.chatUser, this.chatMessages);
|
||||||
|
|
||||||
void add(ChatMessage cm) {
|
void insert(ChatMessage cm) {
|
||||||
this.chatMessages.add(cm);
|
this.chatMessages.insert(0, cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
@ -24,19 +24,15 @@ class ChatModel with ChangeNotifier {
|
|||||||
static final clientModeID = -1;
|
static final clientModeID = -1;
|
||||||
|
|
||||||
final ChatUser me = ChatUser(
|
final ChatUser me = ChatUser(
|
||||||
uid: "",
|
id: "",
|
||||||
name: "Me",
|
firstName: "Me",
|
||||||
);
|
);
|
||||||
|
|
||||||
late final Map<int, MessageBody> _messages = Map()
|
late final Map<int, MessageBody> _messages = Map()
|
||||||
..[clientModeID] = MessageBody(me, []);
|
..[clientModeID] = MessageBody(me, []);
|
||||||
|
|
||||||
final _scroller = ScrollController();
|
|
||||||
|
|
||||||
var _currentID = clientModeID;
|
var _currentID = clientModeID;
|
||||||
|
|
||||||
ScrollController get scroller => _scroller;
|
|
||||||
|
|
||||||
Map<int, MessageBody> get messages => _messages;
|
Map<int, MessageBody> get messages => _messages;
|
||||||
|
|
||||||
int get currentID => _currentID;
|
int get currentID => _currentID;
|
||||||
@ -62,8 +58,8 @@ class ChatModel with ChangeNotifier {
|
|||||||
"Failed to changeCurrentID,remote user doesn't exist");
|
"Failed to changeCurrentID,remote user doesn't exist");
|
||||||
}
|
}
|
||||||
final chatUser = ChatUser(
|
final chatUser = ChatUser(
|
||||||
uid: client.peerId,
|
id: client.peerId,
|
||||||
name: client.name,
|
firstName: client.name,
|
||||||
);
|
);
|
||||||
_messages[id] = MessageBody(chatUser, []);
|
_messages[id] = MessageBody(chatUser, []);
|
||||||
_currentID = id;
|
_currentID = id;
|
||||||
@ -80,48 +76,39 @@ class ChatModel with ChangeNotifier {
|
|||||||
late final chatUser;
|
late final chatUser;
|
||||||
if (id == clientModeID) {
|
if (id == clientModeID) {
|
||||||
chatUser = ChatUser(
|
chatUser = ChatUser(
|
||||||
name: FFI.ffiModel.pi.username,
|
firstName: FFI.ffiModel.pi.username,
|
||||||
uid: FFI.getId(),
|
id: FFI.getId(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final client = FFI.serverModel.clients[id];
|
final client = FFI.serverModel.clients[id];
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return debugPrint("Failed to receive msg,user doesn't exist");
|
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)) {
|
if (!_messages.containsKey(id)) {
|
||||||
_messages[id] = MessageBody(chatUser, []);
|
_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;
|
_currentID = id;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
scrollToBottom();
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToBottom() {
|
|
||||||
Future.delayed(Duration(milliseconds: 500), () {
|
|
||||||
_scroller.animateTo(_scroller.position.maxScrollExtent,
|
|
||||||
duration: Duration(milliseconds: 200),
|
|
||||||
curve: Curves.fastLinearToSlowEaseIn);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
send(ChatMessage message) {
|
send(ChatMessage message) {
|
||||||
if (message.text != null && message.text!.isNotEmpty) {
|
if (message.text.isNotEmpty) {
|
||||||
_messages[_currentID]?.add(message);
|
_messages[_currentID]?.insert(message);
|
||||||
if (_currentID == clientModeID) {
|
if (_currentID == clientModeID) {
|
||||||
FFI.setByName("chat_client_mode", message.text!);
|
FFI.setByName("chat_client_mode", message.text);
|
||||||
} else {
|
} else {
|
||||||
final msg = Map()
|
final msg = Map()
|
||||||
..["id"] = _currentID
|
..["id"] = _currentID
|
||||||
..["text"] = message.text!;
|
..["text"] = message.text;
|
||||||
FFI.setByName("chat_server_mode", jsonEncode(msg));
|
FFI.setByName("chat_server_mode", jsonEncode(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
scrollToBottom();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
@ -199,6 +199,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
|
jobReset();
|
||||||
|
|
||||||
// save config
|
// save config
|
||||||
Map<String, String> msg = Map();
|
Map<String, String> msg = Map();
|
||||||
|
@ -68,7 +68,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
void updatePermission(Map<String, dynamic> evt) {
|
void updatePermission(Map<String, dynamic> evt) {
|
||||||
evt.forEach((k, v) {
|
evt.forEach((k, v) {
|
||||||
if (k == 'name') return;
|
if (k == 'name' || k.isEmpty) return;
|
||||||
_permissions[k] = v == 'true';
|
_permissions[k] = v == 'true';
|
||||||
});
|
});
|
||||||
print('$_permissions');
|
print('$_permissions');
|
||||||
@ -162,6 +162,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
FFI.serverModel.onClientAuthorized(evt);
|
FFI.serverModel.onClientAuthorized(evt);
|
||||||
} else if (name == 'on_client_remove') {
|
} else if (name == 'on_client_remove') {
|
||||||
FFI.serverModel.onClientRemove(evt);
|
FFI.serverModel.onClientRemove(evt);
|
||||||
|
} else if (name == 'update_quality_status') {
|
||||||
|
FFI.qualityMonitorModel.updateQualityStatus(evt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
PlatformFFI.setEventCallback(cb);
|
PlatformFFI.setEventCallback(cb);
|
||||||
@ -193,14 +195,17 @@ class FfiModel with ChangeNotifier {
|
|||||||
wrongPasswordDialog(id);
|
wrongPasswordDialog(id);
|
||||||
} else if (type == 'input-password') {
|
} else if (type == 'input-password') {
|
||||||
enterPasswordDialog(id);
|
enterPasswordDialog(id);
|
||||||
|
} else if (type == 'restarting') {
|
||||||
|
showMsgBox(type, title, text, false, hasCancel: false);
|
||||||
} else {
|
} else {
|
||||||
var hasRetry = evt['hasRetry'] == 'true';
|
var hasRetry = evt['hasRetry'] == 'true';
|
||||||
showMsgBox(type, title, text, hasRetry);
|
showMsgBox(type, title, text, hasRetry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showMsgBox(String type, String title, String text, bool hasRetry) {
|
void showMsgBox(String type, String title, String text, bool hasRetry,
|
||||||
msgBox(type, title, text);
|
{bool? hasCancel}) {
|
||||||
|
msgBox(type, title, text, hasCancel: hasCancel);
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
if (hasRetry) {
|
if (hasRetry) {
|
||||||
_timer = Timer(Duration(seconds: _reconnects), () {
|
_timer = Timer(Duration(seconds: _reconnects), () {
|
||||||
@ -655,6 +660,44 @@ class CursorModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QualityMonitorData {
|
||||||
|
String? speed;
|
||||||
|
String? fps;
|
||||||
|
String? delay;
|
||||||
|
String? targetBitrate;
|
||||||
|
String? codecFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QualityMonitorModel with ChangeNotifier {
|
||||||
|
var _show = FFI.getByName('toggle_option', 'show-quality-monitor') == 'true';
|
||||||
|
final _data = QualityMonitorData();
|
||||||
|
|
||||||
|
bool get show => _show;
|
||||||
|
QualityMonitorData get data => _data;
|
||||||
|
|
||||||
|
checkShowQualityMonitor() {
|
||||||
|
final show =
|
||||||
|
FFI.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) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum MouseButtons { left, right, wheel }
|
enum MouseButtons { left, right, wheel }
|
||||||
|
|
||||||
extension ToString on MouseButtons {
|
extension ToString on MouseButtons {
|
||||||
@ -684,6 +727,7 @@ class FFI {
|
|||||||
static final serverModel = ServerModel();
|
static final serverModel = ServerModel();
|
||||||
static final chatModel = ChatModel();
|
static final chatModel = ChatModel();
|
||||||
static final fileModel = FileModel();
|
static final fileModel = FileModel();
|
||||||
|
static final qualityMonitorModel = QualityMonitorModel();
|
||||||
|
|
||||||
static String getId() {
|
static String getId() {
|
||||||
return getByName('remote_id');
|
return getByName('remote_id');
|
||||||
|
@ -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/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';
|
||||||
@ -6,8 +6,6 @@ import 'package:provider/provider.dart';
|
|||||||
import '../models/model.dart';
|
import '../models/model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
|
|
||||||
ChatPage chatPage = ChatPage();
|
|
||||||
|
|
||||||
class ChatPage extends StatelessWidget implements PageShape {
|
class ChatPage extends StatelessWidget implements PageShape {
|
||||||
@override
|
@override
|
||||||
final title = translate("Chat");
|
final title = translate("Chat");
|
||||||
@ -25,7 +23,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
final id = entry.key;
|
final id = entry.key;
|
||||||
final user = entry.value.chatUser;
|
final user = entry.value.chatUser;
|
||||||
return PopupMenuItem<int>(
|
return PopupMenuItem<int>(
|
||||||
child: Text("${user.name} ${user.uid}"),
|
child: Text("${user.firstName} ${user.id}"),
|
||||||
value: id,
|
value: id,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
@ -46,19 +44,24 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
DashChat(
|
DashChat(
|
||||||
inputContainerStyle: BoxDecoration(color: Colors.white70),
|
|
||||||
sendOnEnter: false,
|
|
||||||
// if true,reload keyboard everytime,need fix
|
|
||||||
onSend: (chatMsg) {
|
onSend: (chatMsg) {
|
||||||
chatModel.send(chatMsg);
|
chatModel.send(chatMsg);
|
||||||
},
|
},
|
||||||
user: chatModel.me,
|
currentUser: chatModel.me,
|
||||||
messages:
|
messages:
|
||||||
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
||||||
[],
|
[],
|
||||||
// default scrollToBottom has bug https://github.com/fayeed/dash_chat/issues/53
|
messageOptions: MessageOptions(
|
||||||
scrollToBottom: false,
|
showOtherUsersAvatar: false,
|
||||||
scrollController: chatModel.scroller,
|
showTime: true,
|
||||||
|
messageDecorationBuilder: (_, __, ___) =>
|
||||||
|
defaultMessageDecoration(
|
||||||
|
color: MyTheme.accent80,
|
||||||
|
borderTopLeft: 8,
|
||||||
|
borderTopRight: 8,
|
||||||
|
borderBottomRight: 8,
|
||||||
|
borderBottomLeft: 8,
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
chatModel.currentID == ChatModel.clientModeID
|
chatModel.currentID == ChatModel.clientModeID
|
||||||
? SizedBox.shrink()
|
? SizedBox.shrink()
|
||||||
@ -70,7 +73,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
color: MyTheme.accent80),
|
color: MyTheme.accent80),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
Text(
|
Text(
|
||||||
"${currentUser.name ?? ""} ${currentUser.uid ?? ""}",
|
"${currentUser.firstName} ${currentUser.id}",
|
||||||
style: TextStyle(color: MyTheme.accent50),
|
style: TextStyle(color: MyTheme.accent50),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -28,6 +28,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
FFI.connect(widget.id, isFileTransfer: true);
|
FFI.connect(widget.id, isFileTransfer: true);
|
||||||
|
showLoading(translate('Connecting...'));
|
||||||
FFI.ffiModel.updateEventListener(widget.id);
|
FFI.ffiModel.updateEventListener(widget.id);
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ abstract class PageShape extends Widget {
|
|||||||
final List<Widget> appBarActions = [];
|
final List<Widget> appBarActions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final homeKey = GlobalKey<_HomePageState>();
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
HomePage({Key? key}) : super(key: key);
|
HomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -23,12 +25,23 @@ class _HomePageState extends State<HomePage> {
|
|||||||
var _selectedIndex = 0;
|
var _selectedIndex = 0;
|
||||||
final List<PageShape> _pages = [];
|
final List<PageShape> _pages = [];
|
||||||
|
|
||||||
|
void refreshPages() {
|
||||||
|
setState(() {
|
||||||
|
initPages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
initPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initPages() {
|
||||||
|
_pages.clear();
|
||||||
_pages.add(ConnectionPage());
|
_pages.add(ConnectionPage());
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
_pages.addAll([chatPage, ServerPage()]);
|
_pages.addAll([ChatPage(), ServerPage()]);
|
||||||
}
|
}
|
||||||
_pages.add(SettingsPage());
|
_pages.add(SettingsPage());
|
||||||
}
|
}
|
||||||
|
@ -592,6 +592,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
ImagePaint(),
|
ImagePaint(),
|
||||||
CursorPaint(),
|
CursorPaint(),
|
||||||
|
QualityMonitor(),
|
||||||
getHelpTools(),
|
getHelpTools(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 0,
|
width: 0,
|
||||||
@ -658,7 +659,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: ([
|
children: ([
|
||||||
Container(width: 100.0, child: Text(translate('OS Password'))),
|
Text(translate('OS Password')),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -693,6 +694,13 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
value: 'block-input'));
|
value: 'block-input'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (FFI.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 {
|
() async {
|
||||||
var value = await showMenu(
|
var value = await showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
@ -726,6 +734,8 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
}
|
}
|
||||||
} else if (value == 'reset_canvas') {
|
} else if (value == 'reset_canvas') {
|
||||||
FFI.cursorModel.reset();
|
FFI.cursorModel.reset();
|
||||||
|
} else if (value == 'restart') {
|
||||||
|
showRestartRemoteDevice(pi, widget.id);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
@ -948,6 +958,47 @@ class ImagePainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QualityMonitor extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => ChangeNotifierProvider.value(
|
||||||
|
value: FFI.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(
|
CheckboxListTile getToggle(
|
||||||
void Function(void Function()) setState, option, name) {
|
void Function(void Function()) setState, option, name) {
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
@ -956,6 +1007,9 @@ CheckboxListTile getToggle(
|
|||||||
setState(() {
|
setState(() {
|
||||||
FFI.setByName('toggle_option', option);
|
FFI.setByName('toggle_option', option);
|
||||||
});
|
});
|
||||||
|
if (option == "show-quality-monitor") {
|
||||||
|
FFI.qualityMonitorModel.checkShowQualityMonitor();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(translate(name)));
|
title: Text(translate(name)));
|
||||||
@ -1058,6 +1112,27 @@ void showOptions() {
|
|||||||
}, clickMaskDismiss: true, backDismiss: true);
|
}, 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) FFI.setByName('restart_remote_device');
|
||||||
|
}
|
||||||
|
|
||||||
void showSetOSPassword(bool login) {
|
void showSetOSPassword(bool login) {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var password = FFI.getByName('peer_option', "os-password");
|
var password = FFI.getByName('peer_option', "os-password");
|
||||||
|
@ -200,7 +200,8 @@ class ServerInfo extends StatelessWidget {
|
|||||||
Icon(Icons.warning_amber_sharp,
|
Icon(Icons.warning_amber_sharp,
|
||||||
color: Colors.redAccent, size: 24),
|
color: Colors.redAccent, size: 24),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
translate("Service is not running"),
|
translate("Service is not running"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: 'WorkSans',
|
fontFamily: 'WorkSans',
|
||||||
@ -208,7 +209,7 @@ class ServerInfo extends StatelessWidget {
|
|||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
color: MyTheme.accent80,
|
color: MyTheme.accent80,
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
@ -316,30 +317,35 @@ class PermissionRow extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Expanded(
|
||||||
children: [
|
flex: 5,
|
||||||
SizedBox(
|
child: FittedBox(
|
||||||
width: 140,
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(name,
|
child: Text(name,
|
||||||
style: TextStyle(fontSize: 16.0, color: MyTheme.accent50))),
|
style:
|
||||||
SizedBox(
|
TextStyle(fontSize: 16.0, color: MyTheme.accent50)))),
|
||||||
width: 50,
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(isOk ? translate("ON") : translate("OFF"),
|
child: Text(isOk ? translate("ON") : translate("OFF"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
color: isOk ? Colors.green : Colors.grey)),
|
color: isOk ? Colors.green : Colors.grey))),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
TextButton(
|
Expanded(
|
||||||
onPressed: onPressed,
|
flex: 3,
|
||||||
child: Text(
|
child: FittedBox(
|
||||||
translate(isOk ? "CLOSE" : "OPEN"),
|
fit: BoxFit.scaleDown,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
alignment: Alignment.centerRight,
|
||||||
)),
|
child: TextButton(
|
||||||
const Divider(height: 0)
|
onPressed: onPressed,
|
||||||
|
child: Text(
|
||||||
|
translate(isOk ? "CLOSE" : "OPEN"),
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
)))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,11 @@ class SettingsPage extends StatefulWidget implements PageShape {
|
|||||||
_SettingsState createState() => _SettingsState();
|
_SettingsState createState() => _SettingsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
const url = 'https://rustdesk.com/';
|
||||||
static const url = 'https://rustdesk.com/';
|
final _hasIgnoreBattery = androidVersion >= 26;
|
||||||
final _hasIgnoreBattery = androidVersion >= 26;
|
var _ignoreBatteryOpt = false;
|
||||||
var _ignoreBatteryOpt = false;
|
|
||||||
|
|
||||||
|
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -146,6 +146,12 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
leading: Icon(Icons.cloud),
|
leading: Icon(Icons.cloud),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
showServerSettings();
|
showServerSettings();
|
||||||
|
}),
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(translate('Language')),
|
||||||
|
leading: Icon(Icons.translate),
|
||||||
|
onPressed: (context) {
|
||||||
|
showLanguageSettings();
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
@ -185,6 +191,42 @@ void showServerSettings() {
|
|||||||
showServerSettingsWithValue(id, relay, key, api);
|
showServerSettingsWithValue(id, relay, key, api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showLanguageSettings() {
|
||||||
|
try {
|
||||||
|
final langs = json.decode(FFI.getByName('langs')) as List<dynamic>;
|
||||||
|
var lang = FFI.getByName('local_option', 'lang');
|
||||||
|
DialogManager.show((setState, close) {
|
||||||
|
final setLang = (v) {
|
||||||
|
if (lang != v) {
|
||||||
|
setState(() {
|
||||||
|
lang = v;
|
||||||
|
});
|
||||||
|
final msg = Map()
|
||||||
|
..['name'] = 'lang'
|
||||||
|
..['value'] = v;
|
||||||
|
FFI.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() {
|
void showAbout() {
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
|
@ -27,7 +27,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
height: height,
|
height: height,
|
||||||
builder: (_, onPanUpdate) {
|
builder: (_, onPanUpdate) {
|
||||||
return isIOS
|
return isIOS
|
||||||
? chatPage
|
? ChatPage()
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: CustomAppBar(
|
appBar: CustomAppBar(
|
||||||
@ -68,7 +68,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: chatPage,
|
body: ChatPage(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,27 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -71,6 +92,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -78,13 +106,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
dash_chat:
|
dash_chat_2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dash_chat
|
name: dash_chat_2
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.16"
|
version: "0.0.12"
|
||||||
device_info:
|
device_info:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -195,6 +223,13 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_blurhash:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_blurhash
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
flutter_breadcrumb:
|
flutter_breadcrumb:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -202,6 +237,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -247,6 +289,13 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -345,6 +394,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
octo_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: octo_image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
package_info:
|
package_info:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -449,16 +505,14 @@ packages:
|
|||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.3"
|
||||||
qr_code_scanner:
|
qr_code_scanner:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
name: qr_code_scanner
|
||||||
ref: fix_break_changes_platform
|
url: "https://pub.dartlang.org"
|
||||||
resolved-ref: "0feca6f15042c279ff575c559a3430df917b623d"
|
source: hosted
|
||||||
url: "https://github.com/Heap-Hop/qr_code_scanner.git"
|
version: "1.0.0"
|
||||||
source: git
|
|
||||||
version: "0.7.0"
|
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -466,6 +520,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.27.5"
|
||||||
settings_ui:
|
settings_ui:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -541,6 +602,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.2"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1+1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -562,6 +637,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0+2"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -583,13 +665,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
transparent_image:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: transparent_image
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.0"
|
|
||||||
tuple:
|
tuple:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -674,6 +749,41 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
video_player:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.5"
|
||||||
|
video_player_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.8"
|
||||||
|
video_player_avfoundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_avfoundation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.5"
|
||||||
|
video_player_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.3"
|
||||||
|
video_player_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.12"
|
||||||
wakelock:
|
wakelock:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -745,5 +855,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.17.0-0 <3.0.0"
|
dart: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
@ -32,7 +32,7 @@ dependencies:
|
|||||||
ffi: ^1.1.2
|
ffi: ^1.1.2
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
external_path: ^1.0.1
|
external_path: ^1.0.1
|
||||||
provider: ^5.0.0
|
provider: ^6.0.3
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
wakelock: ^0.5.2
|
wakelock: ^0.5.2
|
||||||
device_info: ^2.0.2
|
device_info: ^2.0.2
|
||||||
@ -41,15 +41,12 @@ dependencies:
|
|||||||
url_launcher: ^6.0.9
|
url_launcher: ^6.0.9
|
||||||
shared_preferences: ^2.0.6
|
shared_preferences: ^2.0.6
|
||||||
toggle_switch: ^1.4.0
|
toggle_switch: ^1.4.0
|
||||||
dash_chat: ^1.1.16
|
dash_chat_2: ^0.0.12
|
||||||
draggable_float_widget: ^0.0.2
|
draggable_float_widget: ^0.0.2
|
||||||
settings_ui: ^2.0.2
|
settings_ui: ^2.0.2
|
||||||
flutter_breadcrumb: ^1.0.1
|
flutter_breadcrumb: ^1.0.1
|
||||||
http: ^0.13.4
|
http: ^0.13.4
|
||||||
qr_code_scanner:
|
qr_code_scanner: ^1.0.0
|
||||||
git:
|
|
||||||
url: https://github.com/Heap-Hop/qr_code_scanner.git
|
|
||||||
ref: fix_break_changes_platform
|
|
||||||
zxing2: ^0.1.0
|
zxing2: ^0.1.0
|
||||||
image_picker: ^0.8.5
|
image_picker: ^0.8.5
|
||||||
image: ^3.1.3
|
image: ^3.1.3
|
||||||
|
@ -3,7 +3,10 @@ use std::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use hbb_common::{log, message_proto::{VideoFrame, video_frame}};
|
use hbb_common::{
|
||||||
|
log,
|
||||||
|
message_proto::{video_frame, VideoFrame},
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_LATENCY: i64 = 500;
|
const MAX_LATENCY: i64 = 500;
|
||||||
const MIN_LATENCY: i64 = 100;
|
const MIN_LATENCY: i64 = 100;
|
||||||
@ -87,3 +90,12 @@ impl ToString for CodecFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct QualityStatus {
|
||||||
|
pub speed: Option<String>,
|
||||||
|
pub fps: Option<i32>,
|
||||||
|
pub delay: Option<i32>,
|
||||||
|
pub target_bitrate: Option<i32>,
|
||||||
|
pub codec_format: Option<CodecFormat>,
|
||||||
|
}
|
||||||
|
18
src/lang.rs
18
src/lang.rs
@ -8,17 +8,17 @@ mod de;
|
|||||||
mod en;
|
mod en;
|
||||||
mod eo;
|
mod eo;
|
||||||
mod es;
|
mod es;
|
||||||
mod hu;
|
|
||||||
mod fr;
|
mod fr;
|
||||||
|
mod hu;
|
||||||
mod id;
|
mod id;
|
||||||
mod it;
|
mod it;
|
||||||
|
mod pl;
|
||||||
mod ptbr;
|
mod ptbr;
|
||||||
mod ru;
|
mod ru;
|
||||||
mod sk;
|
mod sk;
|
||||||
mod tr;
|
mod tr;
|
||||||
mod tw;
|
mod tw;
|
||||||
mod vn;
|
mod vn;
|
||||||
mod pl;
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref LANGS: Value =
|
pub static ref LANGS: Value =
|
||||||
@ -90,13 +90,15 @@ pub fn translate_locale(name: String, locale: &str) -> String {
|
|||||||
_ => en::T.deref(),
|
_ => en::T.deref(),
|
||||||
};
|
};
|
||||||
if let Some(v) = m.get(&name as &str) {
|
if let Some(v) = m.get(&name as &str) {
|
||||||
v.to_string()
|
if v.is_empty() {
|
||||||
} else {
|
if lang != "en" {
|
||||||
if lang != "en" {
|
if let Some(v) = en::T.get(&name as &str) {
|
||||||
if let Some(v) = en::T.get(&name as &str) {
|
return v.to_string();
|
||||||
return v.to_string();
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return v.to_string();
|
||||||
}
|
}
|
||||||
name
|
|
||||||
}
|
}
|
||||||
|
name
|
||||||
}
|
}
|
||||||
|
298
src/mobile.rs
298
src/mobile.rs
@ -1,12 +1,16 @@
|
|||||||
use crate::client::*;
|
use crate::client::*;
|
||||||
use crate::common::{make_fd_to_json};
|
use crate::common::make_fd_to_json;
|
||||||
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
compress::decompress,
|
compress::decompress,
|
||||||
config::{Config, LocalConfig},
|
config::{Config, LocalConfig},
|
||||||
fs, log,
|
fs,
|
||||||
fs::{can_enable_overwrite_detection, new_send_confirm, DigestCheckResult, get_string, transform_windows_path},
|
fs::{
|
||||||
|
can_enable_overwrite_detection, get_string, new_send_confirm, transform_windows_path,
|
||||||
|
DigestCheckResult,
|
||||||
|
},
|
||||||
|
log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::ConnType,
|
rendezvous_proto::ConnType,
|
||||||
@ -17,6 +21,7 @@ use hbb_common::{
|
|||||||
},
|
},
|
||||||
Stream,
|
Stream,
|
||||||
};
|
};
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{Arc, Mutex, RwLock},
|
||||||
@ -83,6 +88,15 @@ impl Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn restart_remote_device() {
|
||||||
|
if let Some(session) = SESSION.write().unwrap().as_ref() {
|
||||||
|
let mut lc = session.lc.write().unwrap();
|
||||||
|
lc.restarting_remote_device = true;
|
||||||
|
let msg = lc.restart_remote_device();
|
||||||
|
session.send(Data::Message(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn send(data: Data) {
|
fn send(data: Data) {
|
||||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||||
session.send(data);
|
session.send(data);
|
||||||
@ -397,6 +411,26 @@ impl Session {
|
|||||||
log::debug!("{:?}", msg_out);
|
log::debug!("{:?}", msg_out);
|
||||||
self.send_msg(msg_out);
|
self.send_msg(msg_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_quality_status(&self, status: QualityStatus) {
|
||||||
|
const NULL: String = String::new();
|
||||||
|
self.push_event(
|
||||||
|
"update_quality_status",
|
||||||
|
vec![
|
||||||
|
("speed", &status.speed.map_or(NULL, |it| it)),
|
||||||
|
("fps", &status.fps.map_or(NULL, |it| it.to_string())),
|
||||||
|
("delay", &status.delay.map_or(NULL, |it| it.to_string())),
|
||||||
|
(
|
||||||
|
"target_bitrate",
|
||||||
|
&status.target_bitrate.map_or(NULL, |it| it.to_string()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"codec_format",
|
||||||
|
&status.codec_format.map_or(NULL, |it| it.to_string()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileManager for Session {}
|
impl FileManager for Session {}
|
||||||
@ -438,7 +472,11 @@ impl Interface for Session {
|
|||||||
|
|
||||||
if lc.is_file_transfer {
|
if lc.is_file_transfer {
|
||||||
if pi.username.is_empty() {
|
if pi.username.is_empty() {
|
||||||
self.msgbox("error", "Error", "No active console user logged on, please connect and logon first.");
|
self.msgbox(
|
||||||
|
"error",
|
||||||
|
"Error",
|
||||||
|
"No active console user logged on, please connect and logon first.",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -487,7 +525,14 @@ impl Interface for Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
||||||
handle_test_delay(t, peer).await;
|
if !t.from_client {
|
||||||
|
self.update_quality_status(QualityStatus {
|
||||||
|
delay: Some(t.last_delay as _),
|
||||||
|
target_bitrate: Some(t.target_bitrate as _),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
handle_test_delay(t, peer).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,6 +547,9 @@ struct Connection {
|
|||||||
write_jobs: Vec<fs::TransferJob>,
|
write_jobs: Vec<fs::TransferJob>,
|
||||||
timer: Interval,
|
timer: Interval,
|
||||||
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
||||||
|
data_count: Arc<AtomicUsize>,
|
||||||
|
frame_count: Arc<AtomicUsize>,
|
||||||
|
video_format: CodecFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
@ -528,6 +576,9 @@ impl Connection {
|
|||||||
write_jobs: Vec::new(),
|
write_jobs: Vec::new(),
|
||||||
timer: time::interval(SEC30),
|
timer: time::interval(SEC30),
|
||||||
last_update_jobs_status: (Instant::now(), Default::default()),
|
last_update_jobs_status: (Instant::now(), Default::default()),
|
||||||
|
data_count: Arc::new(AtomicUsize::new(0)),
|
||||||
|
frame_count: Arc::new(AtomicUsize::new(0)),
|
||||||
|
video_format: CodecFormat::Unknown,
|
||||||
};
|
};
|
||||||
let key = Config::get_option("key");
|
let key = Config::get_option("key");
|
||||||
let token = Config::get_option("access_token");
|
let token = Config::get_option("access_token");
|
||||||
@ -541,6 +592,9 @@ impl Connection {
|
|||||||
("direct", &direct.to_string()),
|
("direct", &direct.to_string()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut status_timer = time::interval(Duration::new(1, 0));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
res = peer.next() => {
|
res = peer.next() => {
|
||||||
@ -553,14 +607,20 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Ok(ref bytes) => {
|
Ok(ref bytes) => {
|
||||||
last_recv_time = Instant::now();
|
last_recv_time = Instant::now();
|
||||||
|
conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed);
|
||||||
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
|
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::info!("Reset by the peer");
|
if session.lc.read().unwrap().restarting_remote_device {
|
||||||
session.msgbox("error", "Connection Error", "Reset by the peer");
|
log::info!("Restart remote device");
|
||||||
|
session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip");
|
||||||
|
} else {
|
||||||
|
log::info!("Reset by the peer");
|
||||||
|
session.msgbox("error", "Connection Error", "Reset by the peer");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -586,6 +646,16 @@ impl Connection {
|
|||||||
conn.timer = time::interval_at(Instant::now() + SEC30, SEC30);
|
conn.timer = time::interval_at(Instant::now() + SEC30, SEC30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ = status_timer.tick() => {
|
||||||
|
let speed = conn.data_count.swap(0, Ordering::Relaxed);
|
||||||
|
let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32);
|
||||||
|
let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _;
|
||||||
|
conn.session.update_quality_status(QualityStatus {
|
||||||
|
speed:Some(speed),
|
||||||
|
fps:Some(fps),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::debug!("Exit io_loop of id={}", session.id);
|
log::debug!("Exit io_loop of id={}", session.id);
|
||||||
@ -603,10 +673,19 @@ impl Connection {
|
|||||||
if !self.first_frame {
|
if !self.first_frame {
|
||||||
self.first_frame = true;
|
self.first_frame = true;
|
||||||
}
|
}
|
||||||
|
let incomming_format = CodecFormat::from(&vf);
|
||||||
|
if self.video_format != incomming_format {
|
||||||
|
self.video_format = incomming_format.clone();
|
||||||
|
self.session.update_quality_status(QualityStatus {
|
||||||
|
codec_format: Some(incomming_format),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
};
|
||||||
if let (Ok(true), Some(s)) = (
|
if let (Ok(true), Some(s)) = (
|
||||||
self.video_handler.handle_frame(vf),
|
self.video_handler.handle_frame(vf),
|
||||||
RGBA_STREAM.read().unwrap().as_ref(),
|
RGBA_STREAM.read().unwrap().as_ref(),
|
||||||
) {
|
) {
|
||||||
|
self.frame_count.fetch_add(1, Ordering::Relaxed);
|
||||||
s.add(ZeroCopyBuffer(self.video_handler.rgb.clone()));
|
s.add(ZeroCopyBuffer(self.video_handler.rgb.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,113 +743,114 @@ impl Connection {
|
|||||||
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(message::Union::FileResponse(fr)) => match fr.union {
|
Some(message::Union::FileResponse(fr)) => {
|
||||||
Some(file_response::Union::Dir(fd)) => {
|
match fr.union {
|
||||||
let mut entries = fd.entries.to_vec();
|
Some(file_response::Union::Dir(fd)) => {
|
||||||
if self.session.peer_platform() == "Windows" {
|
let mut entries = fd.entries.to_vec();
|
||||||
fs::transform_windows_path(&mut entries);
|
if self.session.peer_platform() == "Windows" {
|
||||||
}
|
fs::transform_windows_path(&mut entries);
|
||||||
let id = fd.id;
|
}
|
||||||
self.session.push_event(
|
let id = fd.id;
|
||||||
"file_dir",
|
self.session.push_event(
|
||||||
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
"file_dir",
|
||||||
);
|
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
||||||
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
);
|
||||||
job.set_files(entries);
|
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
||||||
}
|
job.set_files(entries);
|
||||||
}
|
|
||||||
Some(file_response::Union::Block(block)) => {
|
|
||||||
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
|
||||||
if let Err(_err) = job.write(block, None).await {
|
|
||||||
// to-do: add "skip" for writing job
|
|
||||||
}
|
}
|
||||||
self.update_jobs_status();
|
|
||||||
}
|
}
|
||||||
}
|
Some(file_response::Union::Block(block)) => {
|
||||||
Some(file_response::Union::Done(d)) => {
|
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
if let Err(_err) = job.write(block, None).await {
|
||||||
job.modify_time();
|
// to-do: add "skip" for writing job
|
||||||
fs::remove_job(d.id, &mut self.write_jobs);
|
}
|
||||||
|
self.update_jobs_status();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.handle_job_status(d.id, d.file_num, None);
|
Some(file_response::Union::Done(d)) => {
|
||||||
}
|
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
||||||
Some(file_response::Union::Error(e)) => {
|
job.modify_time();
|
||||||
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
fs::remove_job(d.id, &mut self.write_jobs);
|
||||||
}
|
}
|
||||||
Some(file_response::Union::Digest(digest)) => {
|
self.handle_job_status(d.id, d.file_num, None);
|
||||||
if digest.is_upload {
|
}
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
Some(file_response::Union::Error(e)) => {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
||||||
let read_path = get_string(&job.join(&file.name));
|
}
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
Some(file_response::Union::Digest(digest)) => {
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
if digest.is_upload {
|
||||||
let req = FileTransferSendConfirmRequest {
|
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
||||||
id: digest.id,
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
file_num: digest.file_num,
|
let read_path = get_string(&job.join(&file.name));
|
||||||
union: Some(if overwrite {
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
} else {
|
let req = FileTransferSendConfirmRequest {
|
||||||
file_transfer_send_confirm_request::Union::Skip(
|
id: digest.id,
|
||||||
true,
|
file_num: digest.file_num,
|
||||||
)
|
union: Some(if overwrite {
|
||||||
}),
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
..Default::default()
|
} else {
|
||||||
};
|
file_transfer_send_confirm_request::Union::Skip(
|
||||||
job.confirm(&req);
|
true,
|
||||||
let msg = new_send_confirm(req);
|
)
|
||||||
allow_err!(peer.send(&msg).await);
|
}),
|
||||||
} else {
|
..Default::default()
|
||||||
self.handle_override_file_confirm(
|
};
|
||||||
digest.id,
|
job.confirm(&req);
|
||||||
digest.file_num,
|
let msg = new_send_confirm(req);
|
||||||
read_path,
|
allow_err!(peer.send(&msg).await);
|
||||||
true,
|
} else {
|
||||||
);
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
read_path,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
let write_path = get_string(&job.join(&file.name));
|
||||||
let write_path = get_string(&job.join(&file.name));
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
match fs::is_write_need_confirmation(&write_path, &digest) {
|
||||||
match fs::is_write_need_confirmation(&write_path, &digest) {
|
Ok(res) => match res {
|
||||||
Ok(res) => match res {
|
DigestCheckResult::IsSame => {
|
||||||
DigestCheckResult::IsSame => {
|
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
||||||
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.session.send_msg(msg);
|
|
||||||
}
|
|
||||||
DigestCheckResult::NeedConfirm(digest) => {
|
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
|
||||||
let msg = new_send_confirm(
|
|
||||||
FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
|
||||||
file_num: digest.file_num,
|
|
||||||
union: Some(if overwrite {
|
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
|
||||||
} else {
|
|
||||||
file_transfer_send_confirm_request::Union::Skip(true)
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
} else {
|
|
||||||
self.handle_override_file_confirm(
|
|
||||||
digest.id,
|
|
||||||
digest.file_num,
|
|
||||||
write_path.to_string(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
DigestCheckResult::NeedConfirm(digest) => {
|
||||||
DigestCheckResult::NoSuchFile => {
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
let msg = new_send_confirm(
|
let msg = new_send_confirm(
|
||||||
|
FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(if overwrite {
|
||||||
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
|
} else {
|
||||||
|
file_transfer_send_confirm_request::Union::Skip(true)
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.session.send_msg(msg);
|
||||||
|
} else {
|
||||||
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
write_path.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DigestCheckResult::NoSuchFile => {
|
||||||
|
let msg = new_send_confirm(
|
||||||
FileTransferSendConfirmRequest {
|
FileTransferSendConfirmRequest {
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
@ -778,19 +858,20 @@ impl Connection {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("error recving digest: {}", err);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
println!("error recving digest: {}", err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Some(message::Union::Misc(misc)) => match misc.union {
|
Some(message::Union::Misc(misc)) => match misc.union {
|
||||||
Some(misc::Union::AudioFormat(f)) => {
|
Some(misc::Union::AudioFormat(f)) => {
|
||||||
self.audio_handler.handle_format(f); //
|
self.audio_handler.handle_format(f); //
|
||||||
@ -809,6 +890,7 @@ impl Connection {
|
|||||||
Permission::Keyboard => "keyboard",
|
Permission::Keyboard => "keyboard",
|
||||||
Permission::Clipboard => "clipboard",
|
Permission::Clipboard => "clipboard",
|
||||||
Permission::Audio => "audio",
|
Permission::Audio => "audio",
|
||||||
|
Permission::Restart => "restart",
|
||||||
_ => "",
|
_ => "",
|
||||||
},
|
},
|
||||||
&p.enabled.to_string(),
|
&p.enabled.to_string(),
|
||||||
|
@ -113,6 +113,14 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
|||||||
res = Session::get_option(arg);
|
res = Session::get_option(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"local_option" => {
|
||||||
|
if let Ok(arg) = arg.to_str() {
|
||||||
|
res = LocalConfig::get_option(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"langs" => {
|
||||||
|
res = crate::lang::LANGS.to_string();
|
||||||
|
}
|
||||||
// File Action
|
// File Action
|
||||||
"get_home_dir" => {
|
"get_home_dir" => {
|
||||||
res = fs::get_home_as_string();
|
res = fs::get_home_as_string();
|
||||||
@ -311,9 +319,21 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"local_option" => {
|
||||||
|
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
||||||
|
if let Some(name) = m.get("name") {
|
||||||
|
if let Some(value) = m.get("value") {
|
||||||
|
LocalConfig::set_option(name.to_owned(), value.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"input_os_password" => {
|
"input_os_password" => {
|
||||||
Session::input_os_password(value.to_owned(), true);
|
Session::input_os_password(value.to_owned(), true);
|
||||||
}
|
}
|
||||||
|
"restart_remote_device" => {
|
||||||
|
Session::restart_remote_device();
|
||||||
|
}
|
||||||
// File Action
|
// File Action
|
||||||
"read_remote_dir" => {
|
"read_remote_dir" => {
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
||||||
|
@ -239,15 +239,6 @@ impl sciter::EventHandler for Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct QualityStatus {
|
|
||||||
speed: Option<String>,
|
|
||||||
fps: Option<i32>,
|
|
||||||
delay: Option<i32>,
|
|
||||||
target_bitrate: Option<i32>,
|
|
||||||
codec_format: Option<CodecFormat>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
|
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
|
||||||
let me = Self {
|
let me = Self {
|
||||||
@ -638,8 +629,9 @@ impl Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn restart_remote_device(&mut self) {
|
fn restart_remote_device(&mut self) {
|
||||||
self.lc.write().unwrap().restarting_remote_device = true;
|
let mut lc = self.lc.write().unwrap();
|
||||||
let msg = self.lc.write().unwrap().restart_remote_device();
|
lc.restarting_remote_device = true;
|
||||||
|
let msg = lc.restart_remote_device();
|
||||||
self.send(Data::Message(msg));
|
self.send(Data::Message(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user