From 421db2522b85f51c5148eade9fe4f51083770cfb Mon Sep 17 00:00:00 2001 From: niuhuan Date: Thu, 30 Jun 2022 23:38:42 +0800 Subject: [PATCH] ios pkz --- ios/Podfile.lock | 12 ++++++ ios/Runner/Info.plist | 25 +++++++++--- lib/basic/config/ChooserRoot.dart | 2 +- lib/screens/DownloadImportScreen.dart | 58 +++++++++++++++------------ lib/screens/DownloadListScreen.dart | 17 ++++---- lib/screens/InitScreen.dart | 2 +- lib/screens/PkzArchiveScreen.dart | 30 +++++++++++++- lib/screens/PkzComicInfoScreen.dart | 33 +++++++++++++-- 8 files changed, 132 insertions(+), 47 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 721b95a..f77be5f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - app_links (0.0.1): + - Flutter - DKImagePickerController/Core (4.3.2): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource @@ -46,15 +48,19 @@ PODS: - SDWebImage/Core (5.12.5) - SwiftyGif (5.4.3) - TOCropViewController (2.6.1) + - uri_to_file (0.0.1): + - Flutter - url_launcher_ios (0.0.1): - Flutter DEPENDENCIES: + - app_links (from `.symlinks/plugins/app_links/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`) + - uri_to_file (from `.symlinks/plugins/uri_to_file/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: @@ -66,6 +72,8 @@ SPEC REPOS: - TOCropViewController EXTERNAL SOURCES: + app_links: + :path: ".symlinks/plugins/app_links/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" Flutter: @@ -76,10 +84,13 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_picker_ios/ios" permission_handler: :path: ".symlinks/plugins/permission_handler/ios" + uri_to_file: + :path: ".symlinks/plugins/uri_to_file/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: + app_links: 8941e7bac086136bae70d619141c36f258f4381d DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95 @@ -90,6 +101,7 @@ SPEC CHECKSUMS: SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 + uri_to_file: c4726881848febf4806f0a5707bd12903e02b0f0 url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index e91894c..d1dff17 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,14 +2,25 @@ - LSApplicationCategoryType - public.app-category.entertainment - CFBundleDisplayName - pikapika CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + pikapika + CFBundleDocumentTypes + + + CFBundleTypeName + PKZ Archive + LSHandlerRank + Owner + LSItemContentTypes + + pkz + + + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -26,12 +37,14 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + LSApplicationCategoryType + public.app-category.entertainment LSRequiresIPhoneOS - NSPhotoLibraryUsageDescription - Usage images NSPhotoLibraryAddUsageDescription Save images + NSPhotoLibraryUsageDescription + Usage images UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/lib/basic/config/ChooserRoot.dart b/lib/basic/config/ChooserRoot.dart index 37e37dc..cbf632a 100644 --- a/lib/basic/config/ChooserRoot.dart +++ b/lib/basic/config/ChooserRoot.dart @@ -27,7 +27,7 @@ String _currentChooserRoot() { } else if (Platform.isAndroid) { return '/storage/emulated/0'; } else { - throw 'error'; + return ''; } } return _chooserRoot; diff --git a/lib/screens/DownloadImportScreen.dart b/lib/screens/DownloadImportScreen.dart index 0ee75ef..fe0f8d6 100644 --- a/lib/screens/DownloadImportScreen.dart +++ b/lib/screens/DownloadImportScreen.dart @@ -7,6 +7,7 @@ import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/basic/Method.dart'; import 'package:pikapika/basic/config/ChooserRoot.dart'; +import 'PkzArchiveScreen.dart'; import 'components/ContentLoading.dart'; import 'components/RightClickPop.dart'; @@ -60,13 +61,7 @@ class _DownloadImportScreenState extends State { List actions = []; - if (Platform.isWindows || - Platform.isMacOS || - Platform.isLinux || - Platform.isAndroid) { - actions.add(_fileImportButton()); - } - + actions.add(_fileImportButton()); actions.add(_networkImportButton()); return Scaffold( @@ -101,31 +96,44 @@ class _DownloadImportScreenState extends State { allowMultiple: false, initialDirectory: chooseRoot, type: FileType.custom, - allowedExtensions: ['zip'], + allowedExtensions: ['pkz', 'zip'], allowCompression: false, ); String? path = ls != null && ls.count > 0 ? ls.paths[0] : null; if (path != null) { - try { - setState(() { - _importing = true; - }); - await method.importComicDownload(path); - setState(() { - _importMessage = "导入成功"; - }); - } catch (e) { - setState(() { - _importMessage = "导入失败 $e"; - }); - } finally { - setState(() { - _importing = false; - }); + if (path.endsWith(".pkz")) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => + PkzArchiveScreen(pkzPath: path), + ), + ); + } else if (path.endsWith(".zip")) { + try { + setState(() { + _importing = true; + }); + await method.importComicDownload(path); + setState(() { + _importMessage = "导入成功"; + }); + } catch (e) { + setState(() { + _importMessage = "导入失败 $e"; + }); + } finally { + setState(() { + _importing = false; + }); + } } } }, - child: const Text('选择zip文件进行导入'), + child: const Text( + '选择zip文件进行导入\n选择pkz文件进行阅读', + style: TextStyle(), + textAlign: TextAlign.center, + ), ); } diff --git a/lib/screens/DownloadListScreen.dart b/lib/screens/DownloadListScreen.dart index dfeb7b0..5e8b67f 100644 --- a/lib/screens/DownloadListScreen.dart +++ b/lib/screens/DownloadListScreen.dart @@ -62,8 +62,8 @@ class _DownloadListScreenState extends State { actions: [ exportButton(), importButton(), - pauseButton(), resetFailedButton(), + pauseButton(), ], ), body: FutureBuilder( @@ -147,8 +147,7 @@ class _DownloadListScreenState extends State { } Widget exportButton() { - return MaterialButton( - minWidth: 0, + return IconButton( onPressed: () async { await Navigator.push( context, @@ -157,7 +156,7 @@ class _DownloadListScreenState extends State { ), ); }, - child: Column( + icon: Column( children: [ Expanded(child: Container()), const Icon( @@ -175,8 +174,7 @@ class _DownloadListScreenState extends State { } Widget importButton() { - return MaterialButton( - minWidth: 0, + return IconButton( onPressed: () async { await Navigator.push( context, @@ -188,7 +186,7 @@ class _DownloadListScreenState extends State { _f = method.allDownloads(); }); }, - child: Column( + icon: Column( children: [ Expanded(child: Container()), const Icon( @@ -260,8 +258,7 @@ class _DownloadListScreenState extends State { } Widget resetFailedButton() { - return MaterialButton( - minWidth: 0, + return IconButton( onPressed: () async { await method.resetFailed(); setState(() { @@ -269,7 +266,7 @@ class _DownloadListScreenState extends State { }); defaultToast(context, "所有失败的下载已经恢复"); }, - child: Column( + icon: Column( children: [ Expanded(child: Container()), const Icon( diff --git a/lib/screens/InitScreen.dart b/lib/screens/InitScreen.dart index 69a467f..3a6e98f 100644 --- a/lib/screens/InitScreen.dart +++ b/lib/screens/InitScreen.dart @@ -113,7 +113,7 @@ class _InitScreenState extends State { File file = await toFile(initUrl!); Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (BuildContext context) => - PkzArchiveScreen(pkzPath: file.path), + PkzArchiveScreen(pkzPath: file.path, holdPkz: true), )); return; } diff --git a/lib/screens/PkzArchiveScreen.dart b/lib/screens/PkzArchiveScreen.dart index b0835b4..7aeb014 100644 --- a/lib/screens/PkzArchiveScreen.dart +++ b/lib/screens/PkzArchiveScreen.dart @@ -1,5 +1,7 @@ +import 'dart:async'; import 'dart:io'; +import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:path/path.dart' as p; import 'package:permission_handler/permission_handler.dart'; @@ -7,14 +9,20 @@ import 'package:pikapika/basic/Entities.dart'; import 'package:pikapika/basic/Method.dart'; import 'package:pikapika/screens/components/ContentBuilder.dart'; import 'package:pikapika/screens/components/PkzComicInfoCard.dart'; +import 'package:uri_to_file/uri_to_file.dart'; import '../basic/Navigator.dart'; import 'PkzComicInfoScreen.dart'; class PkzArchiveScreen extends StatefulWidget { + final bool holdPkz; final String pkzPath; - const PkzArchiveScreen({Key? key, required this.pkzPath}) : super(key: key); + const PkzArchiveScreen({ + Key? key, + required this.pkzPath, + this.holdPkz = false, + }) : super(key: key); @override State createState() => _PkzArchiveScreenState(); @@ -25,9 +33,27 @@ class _PkzArchiveScreenState extends State with RouteAware { late String _fileName; late Future _future; late PkzArchive _info; + StreamSubscription? _linkSubscription; @override void initState() { + if (widget.holdPkz) { + final appLinks = AppLinks(); + // todo 不必要cancel 随机监听就好了, APP关闭时销毁, 考虑移动到APP里 + _linkSubscription = appLinks.uriLinkStream.listen((uri) async { + RegExp regExp = RegExp(r"^.*\.pkz$"); + final matches = regExp.allMatches(uri.toString()); + if (matches.isNotEmpty) { + File file = await toFile(uri.toString()); + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => + PkzArchiveScreen(pkzPath: file.path), + ), + ); + } + }); + } _fileName = p.basename(widget.pkzPath); _future = _load(); super.initState(); @@ -41,6 +67,7 @@ class _PkzArchiveScreenState extends State with RouteAware { @override void dispose() { + _linkSubscription?.cancel(); routeObserver.unsubscribe(this); super.dispose(); } @@ -68,6 +95,7 @@ class _PkzArchiveScreenState extends State with RouteAware { builder: (BuildContext context) => PkzComicInfoScreen( pkzPath: widget.pkzPath, pkzComic: _info.comics.first, + holdPkz: true, ), )); } diff --git a/lib/screens/PkzComicInfoScreen.dart b/lib/screens/PkzComicInfoScreen.dart index f3d4d73..ef44bbf 100644 --- a/lib/screens/PkzComicInfoScreen.dart +++ b/lib/screens/PkzComicInfoScreen.dart @@ -1,19 +1,29 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:path/path.dart' as p; import 'package:pikapika/basic/Entities.dart'; import 'package:pikapika/basic/Method.dart'; import 'package:pikapika/screens/PkzReaderScreen.dart'; +import 'package:uri_to_file/uri_to_file.dart'; import '../basic/Navigator.dart'; +import 'PkzArchiveScreen.dart'; import 'components/PkzComicInfoCard.dart'; class PkzComicInfoScreen extends StatefulWidget { + final bool holdPkz; final String pkzPath; final PkzComic pkzComic; - const PkzComicInfoScreen( - {Key? key, required this.pkzPath, required this.pkzComic}) - : super(key: key); + const PkzComicInfoScreen({ + Key? key, + required this.pkzPath, + required this.pkzComic, + this.holdPkz = false, + }) : super(key: key); @override State createState() => _PkzComicInfoScreenState(); @@ -22,9 +32,25 @@ class PkzComicInfoScreen extends StatefulWidget { class _PkzComicInfoScreenState extends State with RouteAware { PkzComicViewLog? _log; + StreamSubscription? _linkSubscription; @override void initState() { + if (widget.holdPkz) { + final appLinks = AppLinks(); + // todo 不必要cancel 随机监听就好了, APP关闭时销毁, 考虑移动到APP里 + _linkSubscription = appLinks.uriLinkStream.listen((uri) async { + RegExp regExp = RegExp(r"^.*\.pkz$"); + final matches = regExp.allMatches(uri.toString()); + if (matches.isNotEmpty) { + File file = await toFile(uri.toString()); + Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) => + PkzArchiveScreen(pkzPath: file.path), + )); + } + }); + } _load(); super.initState(); } @@ -37,6 +63,7 @@ class _PkzComicInfoScreenState extends State @override void dispose() { + _linkSubscription?.cancel(); routeObserver.unsubscribe(this); super.dispose(); }