mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-11-04 00:33:12 +00:00
Merge branch 'master' into login_method
This commit is contained in:
commit
2fd2d89aa9
69 files changed
+526
-44
No files matched your search
@@ -40,7 +40,7 @@ prepare_flutter() {
|
||||
|
||||
# Patch flutter
|
||||
|
||||
if dpkg --compare-versions "${VERSION}" ge "3.24.4"; then
|
||||
if dpkg --compare-versions "${VERSION}" ge "3.24.4" && dpkg --compare-versions "${VERSION}" lt "3.44.0"; then
|
||||
git apply "${ROOTDIR}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff"
|
||||
fi
|
||||
|
||||
@@ -324,11 +324,7 @@ prebuild)
|
||||
|
||||
# Install Flutter bridge version
|
||||
|
||||
# Unbraced on purpose: F-Droid recipes rewrite the braced form with sed to
|
||||
# pin a version, but the bridge generator only works with the Flutter
|
||||
# version bridge.yml uses
|
||||
|
||||
prepare_flutter "$FLUTTER_BRIDGE_VERSION" "${HOME}/flutter"
|
||||
prepare_flutter "${FLUTTER_BRIDGE_VERSION}" "${HOME}/flutter"
|
||||
|
||||
# Save changes
|
||||
|
||||
@@ -346,7 +342,7 @@ prebuild)
|
||||
pushd flutter
|
||||
|
||||
flutter clean
|
||||
flutter packages pub get --enforce-lockfile
|
||||
flutter packages pub get
|
||||
|
||||
popd # flutter
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
5CE7AD012EB4000000A1B2C3 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE7AD002EB4000000A1B2C3 /* SceneDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
@@ -40,6 +41,7 @@
|
||||
4151ACC476A12FDC49BC7860 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
5CE7AD002EB4000000A1B2C3 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
@@ -104,6 +106,7 @@
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
5CE7AD002EB4000000A1B2C3 /* SceneDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
path = Runner;
|
||||
@@ -279,6 +282,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
5CE7AD012EB4000000A1B2C3 /* SceneDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -8,7 +8,6 @@ import AVFoundation
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
do {
|
||||
// Mix remote audio with other apps while still respecting silent mode.
|
||||
try AVAudioSession.sharedInstance().setCategory(.ambient)
|
||||
@@ -18,6 +17,19 @@ import AVFoundation
|
||||
dummyMethodToEnforceBundling();
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
// With the UIScene life cycle, the FlutterViewController only exists once the scene
|
||||
// connects, and FlutterAppDelegate reaches its engine through `window`. A launch URL
|
||||
// also arrives with the scene instead of in launchOptions, so forward it to plugins here.
|
||||
func sceneWillConnect(window: UIWindow?, launchURL: URL?) {
|
||||
self.window = window
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
var launchOptions: [UIApplication.LaunchOptionsKey: Any] = [:]
|
||||
if let launchURL = launchURL {
|
||||
launchOptions[.url] = launchURL
|
||||
}
|
||||
_ = super.application(UIApplication.shared, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
public func dummyMethodToEnforceBundling() {
|
||||
dummy_method_to_enforce_bundling();
|
||||
|
||||
@@ -47,6 +47,27 @@
|
||||
<string>This app needs camera access to scan QR codes</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app needs photo library access to get QR codes from image</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneClassName</key>
|
||||
<string>UIWindowScene</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
@@ -71,7 +92,7 @@
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<true/>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<true/>
|
||||
<key>io.flutter.embedded_views_preview</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import UIKit
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
func scene(
|
||||
_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions
|
||||
) {
|
||||
(UIApplication.shared.delegate as? AppDelegate)?.sceneWillConnect(
|
||||
window: window, launchURL: connectionOptions.urlContexts.first?.url)
|
||||
}
|
||||
|
||||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
||||
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
|
||||
for context in URLContexts {
|
||||
_ = appDelegate.application(UIApplication.shared, open: context.url, options: [:])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/install_directory_confirmation.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
@@ -164,27 +165,46 @@ class _InstallPageBodyState extends State<_InstallPageBody>
|
||||
children: [
|
||||
Text(translate('Installation'),
|
||||
style: Theme.of(context).textTheme.headlineMedium),
|
||||
Row(
|
||||
Table(
|
||||
columnWidths: const {
|
||||
0: IntrinsicColumnWidth(),
|
||||
2: IntrinsicColumnWidth(),
|
||||
},
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
children: [
|
||||
Text('${translate('Installation Path')}:')
|
||||
.marginOnly(right: 10),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(0.75 * em),
|
||||
TableRow(
|
||||
children: [
|
||||
Text('${translate('Installation Path')}:')
|
||||
.marginOnly(right: 10),
|
||||
TextField(
|
||||
controller: controller,
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(0.75 * em),
|
||||
),
|
||||
).workaroundFreezeLinuxMint().marginOnly(right: 10),
|
||||
Obx(
|
||||
() => OutlinedButton.icon(
|
||||
icon: Icon(Icons.folder_outlined, size: 16),
|
||||
onPressed:
|
||||
btnEnabled.value ? selectInstallPath : null,
|
||||
style: buttonStyle,
|
||||
label: Text(translate('Change Path')),
|
||||
),
|
||||
),
|
||||
).workaroundFreezeLinuxMint().marginOnly(right: 10),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
children: [
|
||||
const SizedBox.shrink(),
|
||||
ValueListenableBuilder<TextEditingValue>(
|
||||
valueListenable: controller,
|
||||
builder: (context, value, child) =>
|
||||
InstallDirectoryWarning(directoryPath: value.text),
|
||||
).marginOnly(right: 10),
|
||||
const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
Obx(
|
||||
() => OutlinedButton.icon(
|
||||
icon: Icon(Icons.folder_outlined, size: 16),
|
||||
onPressed: btnEnabled.value ? selectInstallPath : null,
|
||||
style: buttonStyle,
|
||||
label: Text(translate('Change Path')),
|
||||
),
|
||||
)
|
||||
],
|
||||
).marginSymmetric(vertical: 2 * em),
|
||||
Option(startmenu, label: 'Create start menu shortcuts')
|
||||
@@ -277,7 +297,9 @@ class _InstallPageBodyState extends State<_InstallPageBody>
|
||||
));
|
||||
}
|
||||
|
||||
void install() {
|
||||
Future<void> install() async {
|
||||
if (!btnEnabled.value) return;
|
||||
final installPath = controller.text;
|
||||
do_install() {
|
||||
btnEnabled.value = false;
|
||||
cancelEnabled.value = false;
|
||||
@@ -286,10 +308,18 @@ class _InstallPageBodyState extends State<_InstallPageBody>
|
||||
if (startmenu.value) args += ' startmenu';
|
||||
if (desktopicon.value) args += ' desktopicon';
|
||||
if (printer.value) args += ' printer';
|
||||
bind.installInstallMe(options: args, path: controller.text);
|
||||
bind.installInstallMe(options: args, path: installPath);
|
||||
}
|
||||
|
||||
do_install();
|
||||
btnEnabled.value = false;
|
||||
try {
|
||||
final confirmed =
|
||||
await confirmInstallDirectory(this.context, targetPath: installPath);
|
||||
if (!mounted || !confirmed) return;
|
||||
do_install();
|
||||
} finally {
|
||||
if (mounted && !showProgress.value) btnEnabled.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
void selectInstallPath() async {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
const _nonemptyDirectoryWarning = 'install-directory-not-empty-tip';
|
||||
|
||||
Future<bool> confirmInstallDirectory(BuildContext context,
|
||||
{required String targetPath}) async {
|
||||
try {
|
||||
final needsWarning = await _needsDirectoryWarning(targetPath);
|
||||
if (!context.mounted) return false;
|
||||
if (!needsWarning) return true;
|
||||
return await _showDirectoryDialog(
|
||||
context, '${translate(_nonemptyDirectoryWarning)}\n$targetPath');
|
||||
} on FileSystemException catch (error) {
|
||||
debugPrint('Failed to inspect installation directory: $error');
|
||||
if (context.mounted) {
|
||||
await _showDirectoryDialog(context, error.toString(), isError: true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _needsDirectoryWarning(String directoryPath) async {
|
||||
if (bind.mainIsInstalled() &&
|
||||
path.equals(directoryPath, bind.installInstallPath())) {
|
||||
return false;
|
||||
}
|
||||
final directory = Directory(directoryPath);
|
||||
return await directory.exists() &&
|
||||
!await directory.list(followLinks: false).isEmpty;
|
||||
}
|
||||
|
||||
Future<bool> _showDirectoryDialog(BuildContext context, String message,
|
||||
{bool isError = false}) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
scrollable: true,
|
||||
title: Text(translate(isError ? 'Error' : 'Warning')),
|
||||
content: SelectableText(message),
|
||||
actions: [
|
||||
dialogButton(isError ? 'OK' : 'Cancel',
|
||||
isOutline: !isError,
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false)),
|
||||
if (!isError)
|
||||
dialogButton('Continue',
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true)),
|
||||
],
|
||||
),
|
||||
);
|
||||
return confirmed == true;
|
||||
}
|
||||
|
||||
class InstallDirectoryWarning extends StatefulWidget {
|
||||
InstallDirectoryWarning({required this.directoryPath})
|
||||
: super(key: ValueKey(directoryPath));
|
||||
|
||||
final String directoryPath;
|
||||
|
||||
@override
|
||||
State<InstallDirectoryWarning> createState() =>
|
||||
_InstallDirectoryWarningState();
|
||||
}
|
||||
|
||||
class _InstallDirectoryWarningState extends State<InstallDirectoryWarning> {
|
||||
static const _spacing = 8.0;
|
||||
late final Future<bool> _needsWarning =
|
||||
_needsDirectoryWarning(widget.directoryPath);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<bool>(
|
||||
future: _needsWarning,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasError && snapshot.data != true) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: _spacing),
|
||||
child: Text(
|
||||
snapshot.hasError
|
||||
? '${translate('Error')}: ${snapshot.error}'
|
||||
: translate(_nonemptyDirectoryWarning),
|
||||
style: TextStyle(
|
||||
color: snapshot.hasError
|
||||
? Theme.of(context).colorScheme.error
|
||||
: Colors.orange.shade700)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user