rustdesk/flutter/lib/desktop/pages/desktop_home_page.dart

1007 lines
35 KiB
Dart
Raw Normal View History

import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:auto_size_text/auto_size_text.dart';
2023-03-04 14:28:43 +08:00
import 'package:flutter/material.dart';
2022-07-14 12:32:01 +08:00
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/common/widgets/animated_rotation_widget.dart';
import 'package:flutter_hbb/common/widgets/custom_password.dart';
import 'package:flutter_hbb/consts.dart';
2022-05-29 04:39:12 +08:00
import 'package:flutter_hbb/desktop/pages/connection_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
2022-09-28 11:20:57 +08:00
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
2023-05-08 13:10:39 +08:00
import 'package:flutter_hbb/plugin/ui_manager.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart';
2022-07-14 12:32:01 +08:00
import 'package:get/get.dart';
import 'package:provider/provider.dart';
2022-09-25 21:45:37 +08:00
import 'package:url_launcher/url_launcher.dart';
import 'package:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart' as window_size;
2022-05-23 16:44:23 +08:00
2022-09-23 20:18:11 +08:00
import '../widgets/button.dart';
2022-05-23 16:44:23 +08:00
class DesktopHomePage extends StatefulWidget {
const DesktopHomePage({Key? key}) : super(key: key);
2022-05-23 16:44:23 +08:00
@override
State<DesktopHomePage> createState() => _DesktopHomePageState();
2022-05-23 16:44:23 +08:00
}
2022-05-29 19:55:50 +08:00
const borderColor = Color(0xFF2F65BA);
class _DesktopHomePageState extends State<DesktopHomePage>
2022-12-01 11:19:51 +08:00
with AutomaticKeepAliveClientMixin {
2022-09-28 11:20:57 +08:00
final _leftPaneScrollController = ScrollController();
@override
bool get wantKeepAlive => true;
2022-09-23 17:28:22 +08:00
var updateUrl = '';
var systemError = '';
2022-10-18 10:29:33 +08:00
StreamSubscription? _uniLinksSubscription;
var svcStopped = false.obs;
var watchIsCanScreenRecording = false;
var watchIsProcessTrust = false;
2023-01-06 13:19:08 +08:00
var watchIsInputMonitoring = false;
2023-01-31 13:32:10 +08:00
var watchIsCanRecordAudio = false;
Timer? _updateTimer;
bool isCardClosed = false;
final RxBool _editHover = false.obs;
final GlobalKey _childKey = GlobalKey();
2022-05-23 16:44:23 +08:00
@override
Widget build(BuildContext context) {
super.build(context);
2024-03-15 00:26:53 +08:00
final isIncomingOnly = bind.isIncomingOnly();
return Row(
2022-09-28 11:20:57 +08:00
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildLeftPane(context),
2024-03-15 00:26:53 +08:00
if (!isIncomingOnly) const VerticalDivider(width: 1),
if (!isIncomingOnly) Expanded(child: buildRightPane(context)),
],
);
}
2024-03-16 11:52:30 +08:00
Widget buildPresetPasswordWarning() {
return FutureBuilder<bool>(
future: bind.isPresetPassword(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // Show a loading spinner while waiting for the Future to complete
} else if (snapshot.hasError) {
return Text(
'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error
} else if (snapshot.hasData && snapshot.data == true) {
return Container(
color: Colors.yellow,
child: Column(
children: [
Align(
child: Text(
translate("Security Alert"),
style: TextStyle(
color: Colors.red,
fontSize: 20,
fontWeight: FontWeight.bold,
),
)).paddingOnly(bottom: 8),
Text(
translate("preset_password_warning"),
style: TextStyle(color: Colors.red),
)
],
).paddingAll(8),
); // Show a warning message if the Future completed with true
} else {
return SizedBox
.shrink(); // Show nothing if the Future completed with false or null
}
},
);
}
2022-09-28 11:20:57 +08:00
Widget buildLeftPane(BuildContext context) {
2024-03-15 00:26:53 +08:00
final isIncomingOnly = bind.isIncomingOnly();
final isOutgoingOnly = bind.isOutgoingOnly();
final children = <Widget>[
2024-03-16 11:52:30 +08:00
if (!isOutgoingOnly) buildPresetPasswordWarning(),
2024-03-15 00:26:53 +08:00
if (bind.isCustomClient())
Align(
alignment: Alignment.center,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
launchUrl(Uri.parse('https://rustdesk.com'));
},
child: Opacity(
opacity: 0.5,
child: Text(
translate("powered_by_me"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 9, decoration: TextDecoration.underline),
)),
),
).marginOnly(top: 6),
),
2024-03-16 21:55:03 +08:00
Align(
alignment: Alignment.center,
child: loadLogo(),
),
buildTip(context),
2024-03-15 00:26:53 +08:00
if (!isOutgoingOnly) buildIDBoard(context),
if (!isOutgoingOnly) buildPasswordBoard(context),
FutureBuilder<Widget>(
future: buildHelpCards(),
builder: (_, data) {
if (data.hasData) {
2024-03-15 00:26:53 +08:00
if (isIncomingOnly) {
if (isInHomePage()) {
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
}
}
return data.data!;
} else {
return const Offstage();
}
},
),
buildPluginEntry(),
];
2024-03-15 00:26:53 +08:00
if (isIncomingOnly) {
children.addAll([
Divider(),
2024-03-15 09:04:49 +08:00
OnlineStatusWidget(
onSvcStatusChanged: () {
if (isInHomePage()) {
2024-03-15 09:04:49 +08:00
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
}
},
).marginOnly(bottom: 6, right: 6)
]);
}
final textColor = Theme.of(context).textTheme.titleLarge?.color;
return ChangeNotifierProvider.value(
value: gFFI.serverModel,
2022-05-29 10:25:36 +08:00
child: Container(
2024-03-15 00:26:53 +08:00
width: isIncomingOnly ? 280.0 : 200.0,
2023-02-23 23:49:31 +08:00
color: Theme.of(context).colorScheme.background,
2022-09-28 11:20:57 +08:00
child: DesktopScrollWrapper(
scrollController: _leftPaneScrollController,
child: Stack(
children: [
SingleChildScrollView(
controller: _leftPaneScrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: Column(
key: _childKey,
children: children,
),
),
2024-03-15 00:26:53 +08:00
if (isOutgoingOnly)
Positioned(
bottom: 6,
left: 12,
child: Align(
alignment: Alignment.centerLeft,
child: InkWell(
child: Obx(
() => Icon(
Icons.settings,
color: _editHover.value
? textColor
: Colors.grey.withOpacity(0.5),
size: 22,
),
),
2024-03-15 00:26:53 +08:00
onTap: () => DesktopSettingPage.switch2page(0),
onHover: (value) => _editHover.value = value,
),
),
)
],
2022-09-28 11:20:57 +08:00
),
2022-05-29 10:25:36 +08:00
),
),
);
}
2022-09-19 18:38:19 +08:00
buildRightPane(BuildContext context) {
return Container(
2022-09-23 16:31:50 +08:00
color: Theme.of(context).scaffoldBackgroundColor,
child: ConnectionPage(),
);
}
buildIDBoard(BuildContext context) {
final model = gFFI.serverModel;
2022-05-29 10:25:36 +08:00
return Container(
margin: const EdgeInsets.only(left: 20, right: 11),
height: 57,
2022-05-29 10:25:36 +08:00
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Container(
width: 2,
decoration: const BoxDecoration(color: MyTheme.accent),
).marginOnly(top: 5),
2022-05-29 10:25:36 +08:00
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 7),
2022-05-29 10:25:36 +08:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 25,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
translate("ID"),
style: TextStyle(
fontSize: 14,
2022-09-23 16:31:50 +08:00
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.5)),
).marginOnly(top: 5),
buildPopupMenu(context)
],
),
2022-05-29 10:25:36 +08:00
),
Flexible(
child: GestureDetector(
onDoubleTap: () {
Clipboard.setData(
ClipboardData(text: model.serverId.text));
showToast(translate("Copied"));
},
child: TextFormField(
controller: model.serverId,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
2023-03-03 16:50:45 +08:00
contentPadding: EdgeInsets.only(top: 10, bottom: 10),
),
style: TextStyle(
fontSize: 22,
),
),
),
)
2022-05-29 10:25:36 +08:00
],
),
),
2022-05-29 10:25:36 +08:00
),
],
),
);
}
Widget buildPopupMenu(BuildContext context) {
2022-09-23 16:31:50 +08:00
final textColor = Theme.of(context).textTheme.titleLarge?.color;
RxBool hover = false.obs;
return InkWell(
onTap: DesktopTabPage.onAddSetting,
child: Obx(
() => CircleAvatar(
radius: 15,
backgroundColor: hover.value
2022-09-23 16:31:50 +08:00
? Theme.of(context).scaffoldBackgroundColor
2023-02-23 23:49:31 +08:00
: Theme.of(context).colorScheme.background,
child: Tooltip(
message: translate('Settings'),
child: Icon(
Icons.more_vert_outlined,
size: 20,
color: hover.value ? textColor : textColor?.withOpacity(0.5),
)),
),
),
onHover: (value) => hover.value = value,
);
}
2022-05-29 10:25:36 +08:00
buildPasswordBoard(BuildContext context) {
final model = gFFI.serverModel;
RxBool refreshHover = false.obs;
2022-09-19 18:38:19 +08:00
RxBool editHover = false.obs;
2022-09-23 16:31:50 +08:00
final textColor = Theme.of(context).textTheme.titleLarge?.color;
2022-05-29 10:25:36 +08:00
return Container(
margin: EdgeInsets.only(left: 20.0, right: 16, top: 13, bottom: 13),
2022-05-29 10:25:36 +08:00
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Container(
width: 2,
height: 52,
2022-05-29 10:25:36 +08:00
decoration: BoxDecoration(color: MyTheme.accent),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 7),
2022-05-29 10:25:36 +08:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
translate("One-time Password"),
style: TextStyle(
2022-09-23 16:31:50 +08:00
fontSize: 14, color: textColor?.withOpacity(0.5)),
maxLines: 1,
2022-05-29 10:25:36 +08:00
),
Row(
children: [
Expanded(
child: GestureDetector(
onDoubleTap: () {
if (model.verificationMethod !=
kUsePermanentPassword) {
Clipboard.setData(
ClipboardData(text: model.serverPasswd.text));
showToast(translate("Copied"));
}
},
child: TextFormField(
controller: model.serverPasswd,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
2023-03-03 16:50:45 +08:00
contentPadding:
EdgeInsets.only(top: 14, bottom: 10),
),
style: TextStyle(fontSize: 15),
),
),
),
AnimatedRotationWidget(
onPressed: () => bind.mainUpdateTemporaryPassword(),
child: Obx(() => RotatedBox(
quarterTurns: 2,
child: Tooltip(
message: translate('Refresh Password'),
child: Icon(
Icons.refresh,
color: refreshHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)))),
onHover: (value) => refreshHover.value = value,
).marginOnly(right: 8, top: 4),
if (!bind.isDisableSettings())
InkWell(
child: Obx(
() => Tooltip(
message: translate('Change Password'),
child: Icon(
Icons.edit,
color: editHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)).marginOnly(right: 8, top: 4),
),
2024-03-15 00:26:53 +08:00
onTap: () => DesktopSettingPage.switch2page(0),
onHover: (value) => editHover.value = value,
2022-09-19 18:38:19 +08:00
),
],
),
2022-05-29 10:25:36 +08:00
],
),
),
2022-05-29 10:25:36 +08:00
),
],
),
);
}
2024-03-16 21:55:03 +08:00
buildTip(BuildContext context) {
2024-03-15 00:26:53 +08:00
final isOutgoingOnly = bind.isOutgoingOnly();
2022-05-29 10:25:36 +08:00
return Padding(
padding:
const EdgeInsets.only(left: 20.0, right: 16, top: 16.0, bottom: 5),
2022-05-29 10:25:36 +08:00
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
2024-03-15 00:26:53 +08:00
if (!isOutgoingOnly)
Align(
2024-03-15 00:26:53 +08:00
alignment: Alignment.centerLeft,
child: Text(
translate("Your Desktop"),
style: Theme.of(context).textTheme.titleLarge,
),
),
],
2022-05-29 10:25:36 +08:00
),
SizedBox(
height: 10.0,
2022-05-29 10:25:36 +08:00
),
2024-03-15 00:26:53 +08:00
if (!isOutgoingOnly)
Text(
translate("desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
),
2024-03-15 00:26:53 +08:00
if (isOutgoingOnly)
Text(
translate("outgoing_only_desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
),
2022-05-29 10:25:36 +08:00
],
),
);
2022-05-23 16:44:23 +08:00
}
2023-01-31 13:32:10 +08:00
Future<Widget> buildHelpCards() async {
if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
2024-02-25 13:29:06 +08:00
!isCardClosed &&
bind.mainUriPrefixSync().contains('rustdesk')) {
2022-09-25 21:45:37 +08:00
return buildInstallCard(
"Status",
"There is a newer version of ${bind.mainGetAppNameSync()} ${bind.mainGetNewVersion()} available.",
"Click to download", () async {
final Uri url = Uri.parse('https://rustdesk.com/download');
2022-09-25 21:45:37 +08:00
await launchUrl(url);
}, closeButton: true);
2022-09-23 17:28:22 +08:00
}
if (systemError.isNotEmpty) {
return buildInstallCard("", systemError, "", () {});
}
2024-03-15 00:26:53 +08:00
if (Platform.isWindows && !bind.isDisableInstallation()) {
2022-12-04 18:39:59 +08:00
if (!bind.mainIsInstalled()) {
2024-03-15 00:26:53 +08:00
return buildInstallCard(
"", bind.isOutgoingOnly() ? "" : "install_tip", "Install",
() async {
await rustDeskWinManager.closeAllSubWindows();
bind.mainGotoInstall();
});
2022-12-04 18:39:59 +08:00
} else if (bind.mainIsInstalledLowerVersion()) {
return buildInstallCard(
"Status", "Your installation is lower version.", "Click to upgrade",
() async {
await rustDeskWinManager.closeAllSubWindows();
bind.mainUpdateMe();
});
2022-12-04 18:39:59 +08:00
}
} else if (Platform.isMacOS) {
if (!(bind.isOutgoingOnly() ||
bind.mainIsCanScreenRecording(prompt: false))) {
return buildInstallCard("Permissions", "config_screen", "Configure",
() async {
bind.mainIsCanScreenRecording(prompt: true);
watchIsCanScreenRecording = true;
}, help: 'Help', link: translate("doc_mac_permission"));
} else if (!bind.mainIsProcessTrusted(prompt: false)) {
return buildInstallCard("Permissions", "config_acc", "Configure",
() async {
bind.mainIsProcessTrusted(prompt: true);
watchIsProcessTrust = true;
}, help: 'Help', link: translate("doc_mac_permission"));
2023-01-06 13:19:08 +08:00
} else if (!bind.mainIsCanInputMonitoring(prompt: false)) {
return buildInstallCard("Permissions", "config_input", "Configure",
() async {
bind.mainIsCanInputMonitoring(prompt: true);
watchIsInputMonitoring = true;
}, help: 'Help', link: translate("doc_mac_permission"));
} else if (!svcStopped.value &&
bind.mainIsInstalled() &&
!bind.mainIsInstalledDaemon(prompt: false)) {
return buildInstallCard("", "install_daemon_tip", "Install", () async {
bind.mainIsInstalledDaemon(prompt: true);
});
}
2023-02-07 20:38:27 +08:00
//// Disable microphone configuration for macOS. We will request the permission when needed.
// else if ((await osxCanRecordAudio() !=
// PermissionAuthorizeType.authorized)) {
// return buildInstallCard("Permissions", "config_microphone", "Configure",
// () async {
// osxRequestAudio();
// watchIsCanRecordAudio = true;
// });
// }
2022-12-04 18:39:59 +08:00
} else if (Platform.isLinux) {
if (bind.isOutgoingOnly()) {
return Container();
}
final LinuxCards = <Widget>[];
if (bind.isSelinuxEnforcing()) {
// Check is SELinux enforcing, but show user a tip of is SELinux enabled for simple.
final keyShowSelinuxHelpTip = "show-selinux-help-tip";
if (bind.mainGetLocalOption(key: keyShowSelinuxHelpTip) != 'N') {
LinuxCards.add(buildInstallCard(
"Warning",
"selinux_tip",
"",
() async {},
marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
help: 'Help',
link:
'https://rustdesk.com/docs/en/client/linux/#permissions-issue',
closeButton: true,
closeOption: keyShowSelinuxHelpTip,
));
}
}
if (bind.mainCurrentIsWayland()) {
LinuxCards.add(buildInstallCard(
"Warning", "wayland_experiment_tip", "", () async {},
marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
help: 'Help',
2024-02-14 19:49:06 +08:00
link: 'https://rustdesk.com/docs/en/client/linux/#x11-required'));
} else if (bind.mainIsLoginWayland()) {
LinuxCards.add(buildInstallCard("Warning",
"Login screen using Wayland is not supported", "", () async {},
marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
help: 'Help',
2024-02-14 19:49:06 +08:00
link: 'https://rustdesk.com/docs/en/client/linux/#login-screen'));
}
if (LinuxCards.isNotEmpty) {
return Column(
children: LinuxCards,
);
2022-12-04 18:39:59 +08:00
}
}
if (bind.isIncomingOnly()) {
return Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
onPressed: () {
SystemNavigator.pop(); // Close the application
// https://github.com/flutter/flutter/issues/66631
if (Platform.isWindows) {
exit(0);
}
},
child: Text(translate('Quit')),
),
).marginAll(14);
}
2022-09-23 17:28:22 +08:00
return Container();
}
2022-09-25 21:45:37 +08:00
Widget buildInstallCard(String title, String content, String btnText,
GestureTapCallback onPressed,
{double marginTop = 20.0,
String? help,
String? link,
bool? closeButton,
String? closeOption}) {
void closeCard() async {
if (closeOption != null) {
await bind.mainSetLocalOption(key: closeOption, value: 'N');
if (bind.mainGetLocalOption(key: closeOption) == 'N') {
setState(() {
isCardClosed = true;
});
}
} else {
setState(() {
isCardClosed = true;
});
}
}
return Stack(
children: [
Container(
margin: EdgeInsets.fromLTRB(
0, marginTop, 0, bind.isIncomingOnly() ? marginTop : 0),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromARGB(255, 226, 66, 188),
Color.fromARGB(255, 244, 114, 124),
],
)),
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: (title.isNotEmpty
? <Widget>[
Center(
child: Text(
translate(title),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15),
).marginOnly(bottom: 6)),
]
: <Widget>[]) +
<Widget>[
2024-03-15 00:26:53 +08:00
if (content.isNotEmpty)
Text(
translate(content),
style: TextStyle(
height: 1.5,
color: Colors.white,
fontWeight: FontWeight.normal,
fontSize: 13),
).marginOnly(bottom: 20)
] +
(btnText.isNotEmpty
? <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FixedWidthButton(
width: 150,
padding: 8,
isOutline: true,
text: translate(btnText),
textColor: Colors.white,
borderColor: Colors.white,
textSize: 20,
radius: 10,
onTap: onPressed,
)
])
]
: <Widget>[]) +
(help != null
? <Widget>[
Center(
child: InkWell(
onTap: () async =>
await launchUrl(Uri.parse(link!)),
child: Text(
translate(help),
style: TextStyle(
decoration:
TextDecoration.underline,
color: Colors.white,
fontSize: 12),
)).marginOnly(top: 6)),
]
: <Widget>[]))),
),
if (closeButton != null && closeButton == true)
Positioned(
top: 18,
right: 0,
child: IconButton(
icon: Icon(
Icons.close,
color: Colors.white,
size: 20,
),
onPressed: closeCard,
),
),
],
2022-09-23 17:28:22 +08:00
);
}
@override
void initState() {
super.initState();
Timer(const Duration(seconds: 1), () async {
updateUrl = await bind.mainGetSoftwareUpdateUrl();
if (updateUrl.isNotEmpty) setState(() {});
});
2022-11-29 22:36:35 +08:00
_updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
await gFFI.serverModel.fetchID();
final error = await bind.mainGetError();
if (systemError != error) {
systemError = error;
setState(() {});
}
final v = await bind.mainGetOption(key: "stop-service") == "Y";
if (v != svcStopped.value) {
svcStopped.value = v;
setState(() {});
}
if (watchIsCanScreenRecording) {
if (bind.mainIsCanScreenRecording(prompt: false)) {
watchIsCanScreenRecording = false;
setState(() {});
}
}
if (watchIsProcessTrust) {
if (bind.mainIsProcessTrusted(prompt: false)) {
watchIsProcessTrust = false;
setState(() {});
}
}
2023-01-06 13:19:08 +08:00
if (watchIsInputMonitoring) {
if (bind.mainIsCanInputMonitoring(prompt: false)) {
watchIsInputMonitoring = false;
// Do not notify for now.
// Monitoring may not take effect until the process is restarted.
// rustDeskWinManager.call(
// WindowType.RemoteDesktop, kWindowDisableGrabKeyboard, '');
2023-01-06 13:19:08 +08:00
setState(() {});
}
}
2023-01-31 13:32:10 +08:00
if (watchIsCanRecordAudio) {
if (Platform.isMacOS) {
Future.microtask(() async {
if ((await osxCanRecordAudio() ==
PermissionAuthorizeType.authorized)) {
watchIsCanRecordAudio = false;
setState(() {});
}
});
} else {
watchIsCanRecordAudio = false;
setState(() {});
}
}
2022-09-23 17:28:22 +08:00
});
Get.put<RxBool>(svcStopped, tag: 'stop-service');
2022-11-05 23:41:22 +08:00
rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged);
screenToMap(window_size.Screen screen) => {
'frame': {
'l': screen.frame.left,
't': screen.frame.top,
'r': screen.frame.right,
'b': screen.frame.bottom,
},
'visibleFrame': {
'l': screen.visibleFrame.left,
't': screen.visibleFrame.top,
'r': screen.visibleFrame.right,
'b': screen.visibleFrame.bottom,
},
'scaleFactor': screen.scaleFactor,
};
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
debugPrint(
2022-11-05 23:41:22 +08:00
"[Main] call ${call.method} with args ${call.arguments} from window $fromWindowId");
if (call.method == kWindowMainWindowOnTop) {
windowOnTop(null);
} else if (call.method == kWindowGetWindowInfo) {
final screen = (await window_size.getWindowInfo()).screen;
if (screen == null) {
return '';
} else {
return jsonEncode(screenToMap(screen));
}
} else if (call.method == kWindowGetScreenList) {
return jsonEncode(
(await window_size.getScreenList()).map(screenToMap).toList());
} else if (call.method == kWindowActionRebuild) {
reloadCurrentWindow();
2022-11-05 23:41:22 +08:00
} else if (call.method == kWindowEventShow) {
await rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
2022-11-05 23:41:22 +08:00
} else if (call.method == kWindowEventHide) {
await rustDeskWinManager.unregisterActiveWindow(call.arguments['id']);
} else if (call.method == kWindowConnect) {
await connectMainDesktop(
call.arguments['id'],
isFileTransfer: call.arguments['isFileTransfer'],
isTcpTunneling: call.arguments['isTcpTunneling'],
isRDP: call.arguments['isRDP'],
forceRelay: call.arguments['forceRelay'],
);
} else if (call.method == kWindowEventMoveTabToNewWindow) {
final args = call.arguments.split(',');
int? windowId;
try {
windowId = int.parse(args[0]);
} catch (e) {
debugPrint("Failed to parse window id '${call.arguments}': $e");
}
if (windowId != null) {
await rustDeskWinManager.moveTabToNewWindow(
windowId, args[1], args[2]);
}
} else if (call.method == kWindowEventOpenMonitorSession) {
final args = jsonDecode(call.arguments);
final windowId = args['window_id'] as int;
final peerId = args['peer_id'] as String;
final display = args['display'] as int;
final displayCount = args['display_count'] as int;
final screenRect = parseParamScreenRect(args);
await rustDeskWinManager.openMonitorSession(
windowId, peerId, display, displayCount, screenRect);
}
});
2022-10-18 10:29:33 +08:00
_uniLinksSubscription = listenUniLinks();
2024-03-12 21:47:29 +08:00
if (bind.isIncomingOnly()) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateWindowSize();
});
}
}
_updateWindowSize() {
RenderObject? renderObject = _childKey.currentContext?.findRenderObject();
if (renderObject == null) {
return;
}
if (renderObject is RenderBox) {
final size = renderObject.size;
if (size != imcomingOnlyHomeSize) {
imcomingOnlyHomeSize = size;
windowManager.setSize(getIncomingOnlyHomeSize());
}
}
}
@override
void dispose() {
2022-10-18 10:29:33 +08:00
_uniLinksSubscription?.cancel();
Get.delete<RxBool>(tag: 'stop-service');
_updateTimer?.cancel();
super.dispose();
}
2023-05-08 13:10:39 +08:00
Widget buildPluginEntry() {
final entries = PluginUiManager.instance.entries.entries;
return Offstage(
offstage: entries.isEmpty,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...entries.map((entry) {
2023-05-08 13:10:39 +08:00
return entry.value;
})
],
),
);
}
2022-05-23 16:44:23 +08:00
}
void setPasswordDialog() async {
final pw = await bind.mainGetPermanentPassword();
final p0 = TextEditingController(text: pw);
final p1 = TextEditingController(text: pw);
var errMsg0 = "";
var errMsg1 = "";
final RxString rxPass = pw.trim().obs;
final rules = [
DigitValidationRule(),
UppercaseValidationRule(),
LowercaseValidationRule(),
// SpecialCharacterValidationRule(),
MinCharactersValidationRule(8),
];
2023-05-08 12:34:19 +08:00
gFFI.dialogManager.show((setState, close, context) {
submit() {
setState(() {
errMsg0 = "";
errMsg1 = "";
});
final pass = p0.text.trim();
if (pass.isNotEmpty) {
2023-02-17 02:20:26 +08:00
final Iterable violations = rules.where((r) => !r.validate(pass));
if (violations.isNotEmpty) {
setState(() {
errMsg0 =
'${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}';
});
return;
}
}
if (p1.text.trim() != pass) {
setState(() {
errMsg1 =
'${translate('Prompt')}: ${translate("The confirmation is not identical.")}';
});
return;
}
bind.mainSetPermanentPassword(password: pass);
close();
}
return CustomAlertDialog(
title: Text(translate("Set Password")),
content: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 500),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 8.0,
),
Row(
children: [
Expanded(
child: TextField(
obscureText: true,
decoration: InputDecoration(
labelText: translate('Password'),
errorText: errMsg0.isNotEmpty ? errMsg0 : null),
controller: p0,
autofocus: true,
onChanged: (value) {
rxPass.value = value.trim();
2023-02-17 02:20:26 +08:00
setState(() {
errMsg0 = '';
});
},
),
),
],
),
Row(
children: [
Expanded(child: PasswordStrengthIndicator(password: rxPass)),
],
).marginSymmetric(vertical: 8),
const SizedBox(
height: 8.0,
),
Row(
children: [
Expanded(
child: TextField(
obscureText: true,
decoration: InputDecoration(
labelText: translate('Confirmation'),
errorText: errMsg1.isNotEmpty ? errMsg1 : null),
controller: p1,
2023-02-17 02:20:26 +08:00
onChanged: (value) {
setState(() {
errMsg1 = '';
});
},
),
),
],
),
const SizedBox(
height: 8.0,
),
Obx(() => Wrap(
runSpacing: 8,
spacing: 4,
children: rules.map((e) {
var checked = e.validate(rxPass.value.trim());
return Chip(
label: Text(
e.name,
style: TextStyle(
color: checked
? const Color(0xFF0A9471)
: Color.fromARGB(255, 198, 86, 157)),
),
backgroundColor: checked
? const Color(0xFFD0F7ED)
: Color.fromARGB(255, 247, 205, 232));
}).toList(),
))
],
),
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: submit),
],
onSubmit: submit,
onCancel: close,
);
});
}