diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 76af022d..95e4293b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,6 +4,8 @@ PODS: - Flutter (1.0.0) - flutter_icmp_ping (0.0.1): - Flutter + - open_file (0.0.1): + - Flutter - path_provider_ios (0.0.1): - Flutter - r_upgrade (0.0.1): @@ -15,6 +17,7 @@ DEPENDENCIES: - countly_flutter (from `.symlinks/plugins/countly_flutter/ios`) - Flutter (from `Flutter`) - flutter_icmp_ping (from `.symlinks/plugins/flutter_icmp_ping/ios`) + - open_file (from `.symlinks/plugins/open_file/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - r_upgrade (from `.symlinks/plugins/r_upgrade/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -26,6 +29,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_icmp_ping: :path: ".symlinks/plugins/flutter_icmp_ping/ios" + open_file: + :path: ".symlinks/plugins/open_file/ios" path_provider_ios: :path: ".symlinks/plugins/path_provider_ios/ios" r_upgrade: @@ -37,6 +42,7 @@ SPEC CHECKSUMS: countly_flutter: 38419412e193a1faa5babeb5d28a63fda260687d Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a flutter_icmp_ping: 07e508847df7fa9262d050bb0b203de074bbe517 + open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114 url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 21b7220b..451d1f7f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 101; + CURRENT_PROJECT_VERSION = 108; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -362,7 +362,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.101; + MARKETING_VERSION = 1.0.108; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -484,7 +484,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 101; + CURRENT_PROJECT_VERSION = 108; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -492,7 +492,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.101; + MARKETING_VERSION = 1.0.108; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -508,7 +508,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 101; + CURRENT_PROJECT_VERSION = 108; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -516,7 +516,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.101; + MARKETING_VERSION = 1.0.108; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 4bcc21b2..6eba8ce4 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,9 +2,9 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 107; + static const int build = 108; static const String engine = "Flutter 2.10.3 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision 7e9793dee1 (8 days ago) • 2022-03-02 11:23:12 -0600\nEngine • revision bd539267b4\nTools • Dart 2.16.1 • DevTools 2.9.2\n"; - static const String buildAt = "2022-03-10 13:25:24.362670"; - static const int modifications = 11; + static const String buildAt = "2022-03-10 15:25:32.032568"; + static const int modifications = 0; } diff --git a/lib/view/page/ping.dart b/lib/view/page/ping.dart index 458d39d0..602bfe6b 100644 --- a/lib/view/page/ping.dart +++ b/lib/view/page/ping.dart @@ -18,8 +18,8 @@ class PingPage extends StatefulWidget { class _PingPageState extends State with AutomaticKeepAliveClientMixin { late TextEditingController _textEditingController; - String _result = ''; Ping? _ping; + late MediaQueryData _media; @override void initState() { @@ -33,6 +33,7 @@ class _PingPageState extends State @override void didChangeDependencies() { super.didChangeDependencies(); + _media = MediaQuery.of(context); } @override @@ -46,13 +47,12 @@ class _PingPageState extends State const SizedBox(height: 13), buildInput(context, _textEditingController, maxLines: 1), _buildControl(), - RoundRectCard( - SizedBox( - width: double.infinity, - child: Padding( - padding: const EdgeInsets.all(7), child: Text(_result)), + RoundRectCard(ConstrainedBox( + constraints: BoxConstraints( + minWidth: double.infinity, + minHeight: _media.size.height * 0.6, ), - ), + )), ])), onTap: () => FocusScope.of(context).requestFocus(FocusNode()), ), @@ -60,12 +60,10 @@ class _PingPageState extends State } void doPing() { - _result = ''; _ping = Ping(_textEditingController.text.trim()); _ping!.stream.listen((event) { final resp = event.response.toString(); if (resp == 'null') return; - _result += '$resp\n'; setState(() {}); }); } diff --git a/lib/view/page/sftp.dart b/lib/view/page/sftp.dart index 31eee464..c8a1eb3e 100644 --- a/lib/view/page/sftp.dart +++ b/lib/view/page/sftp.dart @@ -1,5 +1,8 @@ +import 'dart:io'; + import 'package:dartssh2/dartssh2.dart'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:toolbox/core/utils.dart'; import 'package:toolbox/data/model/server/server_connection_state.dart'; import 'package:toolbox/data/model/server/server_private_info.dart'; @@ -7,6 +10,7 @@ import 'package:toolbox/data/model/sftp/absolute_path.dart'; import 'package:toolbox/data/model/sftp/sftp_side_status.dart'; import 'package:toolbox/data/provider/server.dart'; import 'package:toolbox/locator.dart'; +import 'package:toolbox/view/widget/center_loading.dart'; import 'package:toolbox/view/widget/fade_in.dart'; import 'package:toolbox/view/widget/two_line_text.dart'; @@ -144,6 +148,11 @@ class _SFTPPageState extends State { title: const Text('Rename'), onTap: () => rename(context, file), ), + ListTile( + leading: const Icon(Icons.download), + title: const Text('Download'), + onTap: () => download(context, file), + ) ], ), [ @@ -153,6 +162,42 @@ class _SFTPPageState extends State { ]); } + void download(BuildContext context, SftpName name) { + showRoundDialog( + context, 'Download', Text('Download ${name.filename} to local?'), [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel')), + TextButton( + onPressed: () async { + var result = ''; + try { + Navigator.of(context).pop(); + showRoundDialog(context, name.filename, centerSizedLoading, [], + barrierDismiss: false); + final path = await getApplicationDocumentsDirectory(); + final localFile = File('${path.path}/${name.filename}'); + final remotePath = _status.path!.path + '/' + name.filename; + final file = await _status.client?.open(remotePath); + localFile.writeAsBytes(await file!.readBytes()); + Navigator.of(context).pop(); + } catch (e) { + result = e.toString(); + } finally { + if (result.isEmpty) { + result = 'Donwloaded successfully.'; + } + showRoundDialog(context, 'Result', Text(result), [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK')) + ]); + } + }, + child: const Text('Download')) + ]); + } + void delete(BuildContext context, SftpName file) { Navigator.of(context).pop(); showRoundDialog( @@ -274,15 +319,26 @@ class _SFTPPageState extends State { final sftpc = await client.sftp(); _status.client = sftpc; } - final fs = - await _status.client!.listdir(path ?? (_status.path?.path ?? '/')); - fs.sort((a, b) => a.filename.compareTo(b.filename)); - fs.removeAt(0); - if (mounted) { - setState(() { - _status.files = fs; - _status.isBusy = false; - }); + try { + final fs = + await _status.client!.listdir(path ?? (_status.path?.path ?? '/')); + fs.sort((a, b) => a.filename.compareTo(b.filename)); + fs.removeAt(0); + if (mounted) { + setState(() { + _status.files = fs; + _status.isBusy = false; + }); + } + } catch (e) { + await showRoundDialog(context, 'Error', Text(e.toString()), [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK')) + ]); + if (_status.path!.undo()) { + await listDir(); + } } } diff --git a/lib/view/widget/center_loading.dart b/lib/view/widget/center_loading.dart index d260d258..49db55ae 100644 --- a/lib/view/widget/center_loading.dart +++ b/lib/view/widget/center_loading.dart @@ -1,3 +1,11 @@ import 'package:flutter/material.dart'; const centerLoading = Center(child: CircularProgressIndicator()); + +const centerSizedLoading = SizedBox( + width: 77, + height: 77, + child: Center( + child: CircularProgressIndicator(), + ), +); diff --git a/pubspec.lock b/pubspec.lock index 2716724a..1e08e439 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -335,6 +335,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -349,6 +356,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + open_file: + dependency: "direct main" + description: + name: open_file + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" path: dependency: transitive description: @@ -357,7 +371,7 @@ packages: source: hosted version: "1.8.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" @@ -521,7 +535,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.8" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 616eac6b..c10586da 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,8 @@ dependencies: dart_ping_ios: ^1.0.0 dropdown_button2: ^1.1.1 flutter_advanced_drawer: ^1.3.0 + open_file: ^3.2.1 + path_provider: ^2.0.9 dev_dependencies: