♻️ Import/export ui style

This commit is contained in:
niuhuan 2023-03-30 22:38:28 +08:00
parent 73b2ded22a
commit a2430c2541
16 changed files with 733 additions and 796 deletions

View File

@ -103,6 +103,10 @@ class MainActivity : FlutterActivity() {
"androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!) "androidSecureFlag" -> androidSecureFlag(call.argument("flag")!!)
"verifyAuthentication" -> auth() "verifyAuthentication" -> auth()
"androidStorageRoot" -> storageRoot() "androidStorageRoot" -> storageRoot()
"androidDefaultExportsDir" -> androidDefaultExportsDir().absolutePath
"androidMkdirs" -> androidMkdirs(
call.arguments<String>() ?: throw Exception("need arg")
)
else -> { else -> {
notImplementedToken notImplementedToken
} }
@ -408,4 +412,24 @@ class MainActivity : FlutterActivity() {
fun storageRoot(): String { fun storageRoot(): String {
return Environment.getExternalStorageDirectory().absolutePath return Environment.getExternalStorageDirectory().absolutePath
} }
private fun downloadsDir(): File {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
?: throw java.lang.IllegalStateException()
}
private fun defaultPikapikaDir(): File {
return File(downloadsDir(), "pikapika")
}
private fun androidDefaultExportsDir(): File {
return File(defaultPikapikaDir(), "exports")
}
private fun androidMkdirs(path: String) {
val dir = File(path)
if (!dir.exists()) {
dir.mkdirs()
}
}
} }

View File

@ -1 +1 @@
v1.6.8 v1.7.0

View File

@ -1,3 +1,10 @@
v1.7.0
- [x] ✨固定导出路径
- [x] ✨ISO可以直接导出漫画到文件浏览器
- [x] ♻️在首页选择分流亦可测速
- [x] ♻导入导出文案以及UI的优化
v1.6.8 v1.6.8
- [x] ✨选择API分流以及图片分流时进行测速 - [x] ✨选择API分流以及图片分流时进行测速

View File

@ -993,4 +993,20 @@ class Method {
Future stopWebServer() { Future stopWebServer() {
return _flatInvoke("stopWebServer", ""); return _flatInvoke("stopWebServer", "");
} }
Future<String> androidDefaultExportsDir() async {
return await _channel.invokeMethod("androidDefaultExportsDir");
}
Future getHomeDir() {
return _flatInvoke("getHomeDir", "");
}
Future mkdirs(String path) {
return _flatInvoke("mkdirs", path);
}
Future androidMkdirs(String path) async {
return await _channel.invokeMethod("androidMkdirs", path);
}
} }

View File

@ -31,35 +31,6 @@ String currentAddress() => _currentAddress;
String currentAddressName() => _addresses[_currentAddress] ?? ""; String currentAddressName() => _addresses[_currentAddress] ?? "";
Future<void> chooseAddress(BuildContext context) async {
String? choose = await showDialog<String>(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text('选择分流'),
children: <Widget>[
..._addresses.entries.map(
(e) => SimpleDialogOption(
child: ApiOptionRow(
e.value,
e.key,
key: Key("API:${e.key}"),
),
onPressed: () {
Navigator.of(context).pop(e.key);
},
),
),
],
);
},
);
if (choose != null) {
await method.setSwitchAddress(choose);
_currentAddress = choose;
}
}
Widget switchAddressSetting() { Widget switchAddressSetting() {
return StatefulBuilder( return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) { builder: (BuildContext context, void Function(void Function()) setState) {
@ -67,7 +38,7 @@ Widget switchAddressSetting() {
title: const Text("分流"), title: const Text("分流"),
subtitle: Text(currentAddressName()), subtitle: Text(currentAddressName()),
onTap: () async { onTap: () async {
await chooseAddress(context); await chooseAddressAndSwitch(context);
setState(() {}); setState(() {});
}, },
); );
@ -119,7 +90,11 @@ Future chooseAddressAndSwitch(BuildContext context) async {
children: <Widget>[ children: <Widget>[
..._addresses.entries.map( ..._addresses.entries.map(
(e) => SimpleDialogOption( (e) => SimpleDialogOption(
child: Text(e.value), child: ApiOptionRow(
e.value,
e.key,
key: Key("API:${e.key}"),
),
onPressed: () { onPressed: () {
Navigator.of(context).pop(e.key); Navigator.of(context).pop(e.key);
}, },

View File

@ -10,30 +10,22 @@ import '../Method.dart';
const _propertyName = "chooserRoot"; const _propertyName = "chooserRoot";
late String _chooserRoot; late String _chooserRoot;
late String _androidDefaultRoot;
Future<dynamic> initChooserRoot() async { Future<dynamic> initChooserRoot() async {
_chooserRoot = await method.loadProperty(_propertyName, ""); _chooserRoot = await method.loadProperty(_propertyName, "");
if (_chooserRoot.isEmpty) {
if (Platform.isAndroid) { if (Platform.isAndroid) {
_androidDefaultRoot = await method.androidStorageRoot(); try {
_chooserRoot = await method.androidStorageRoot();
} catch (e) {
_chooserRoot = "/sdcard";
}
} else if (Platform.isMacOS || Platform.isLinux) {
_chooserRoot = await method.getHomeDir();
} else if (Platform.isWindows) {
_chooserRoot = "/";
} }
} }
String _currentChooserRoot() {
if (_chooserRoot == "") {
if (Platform.isWindows) {
return '/';
} else if (Platform.isMacOS) {
return '/Users';
} else if (Platform.isLinux) {
return '/';
} else if (Platform.isAndroid) {
return _androidDefaultRoot;
} else {
return '';
}
}
return _chooserRoot;
} }
Future<String> currentChooserRoot() async { Future<String> currentChooserRoot() async {
@ -42,7 +34,7 @@ Future<String> currentChooserRoot() async {
throw Exception("申请权限被拒绝"); throw Exception("申请权限被拒绝");
} }
} }
return _currentChooserRoot(); return _chooserRoot;
} }
Future<dynamic> _inputChooserRoot(BuildContext context) async { Future<dynamic> _inputChooserRoot(BuildContext context) async {
@ -67,7 +59,7 @@ Widget chooserRootSetting() {
builder: (BuildContext context, void Function(void Function()) setState) { builder: (BuildContext context, void Function(void Function()) setState) {
return ListTile( return ListTile(
title: const Text("文件夹选择器默认路径"), title: const Text("文件夹选择器默认路径"),
subtitle: Text(_currentChooserRoot()), subtitle: Text(_chooserRoot),
onTap: () async { onTap: () async {
await _inputChooserRoot(context); await _inputChooserRoot(context);
setState(() {}); setState(() {});

View File

@ -0,0 +1,120 @@
///
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import '../Cross.dart';
import '../Method.dart';
const _propertyName = "exportPath";
late String _exportPath;
Future<dynamic> initExportPath() async {
_exportPath = await method.loadProperty(_propertyName, "");
if (_exportPath.isEmpty) {
if (Platform.isAndroid) {
try {
_exportPath = await method.androidDefaultExportsDir();
} catch (e) {
_exportPath = "/sdcard/Download/pikapika/exports";
}
} else if (Platform.isMacOS || Platform.isLinux) {
_exportPath = await method.getHomeDir();
if (Platform.isMacOS) {
_exportPath = _exportPath + "/Downloads";
}
} else if (Platform.isWindows) {
_exportPath = "exports";
}
}
}
Future<String> attachExportPath() async {
late String path;
if (Platform.isIOS) {
path = await method.iosGetDocumentDir();
} else {
if (Platform.isAndroid) {
if (!(await Permission.storage.request()).isGranted) {
throw Exception("申请权限被拒绝");
}
}
path = _exportPath;
}
if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
await method.mkdirs(path);
} else if (Platform.isAndroid) {
await method.androidMkdirs(path);
}
return path;
}
String showExportPath() {
if (Platform.isIOS) {
return "\n\n随后可在文件管理中找到导出的内容";
}
return "\n\n$_exportPath";
}
Future _setExportPath(String folder) async {
await method.saveProperty(_propertyName, folder);
_exportPath = folder;
}
Widget displayExportPathInfo() {
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
if (Platform.isIOS) {
return Container(
margin: const EdgeInsets.all(15),
padding: const EdgeInsets.all(15),
color: (Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
.withOpacity(.01),
child: const Text("您正在使用iOS设备:\n导出到文件的内容请打开系统自带文件管理进行浏览"),
);
}
return Column(children: [
MaterialButton(
onPressed: () async {
String? choose = await chooseFolder(context);
if (choose != null) {
_setExportPath(choose);
}
setState(() {});
},
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth,
padding: const EdgeInsets.only(top: 15, bottom: 15),
color: (Theme.of(context).textTheme.bodyText1?.color ??
Colors.black)
.withOpacity(.05),
child: Text(
"导出路径 (点击可修改):\n"
"$_exportPath",
textAlign: TextAlign.center,
),
);
},
),
),
...Platform.isAndroid
? [
Container(height: 15),
Container(
margin: const EdgeInsets.all(15),
padding: const EdgeInsets.all(15),
color: (Theme.of(context).textTheme.bodyText1?.color ??
Colors.black)
.withOpacity(.01),
child: const Text(
"您正在使用安卓设备:\n如果不能成功导出并且提示权限不足, 可以尝试在Download或Document下建立子目录进行导出",
),
),
]
: [],
]);
},
);
}

View File

@ -0,0 +1,22 @@
import 'dart:io';
import 'package:flutter/material.dart';
Widget importNotice(BuildContext context) {
if (Platform.isAndroid) {
return Container(
margin: const EdgeInsets.all(15),
padding: const EdgeInsets.all(15),
color: (Theme
.of(context)
.textTheme
.bodyText1
?.color ?? Colors.black)
.withOpacity(.01),
child: const Text(
"您正在使用安卓设备:\n如果不能导入导出并且提示权限不足, 可以尝试在Download或Document下建立子目录进行导入",
),
);
}
return Container();
}

View File

@ -1,17 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:pikapika/basic/Channels.dart'; import 'package:pikapika/basic/Channels.dart';
import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/basic/Common.dart';
import 'package:pikapika/basic/Cross.dart';
import 'package:pikapika/basic/Entities.dart'; import 'package:pikapika/basic/Entities.dart';
import 'package:pikapika/basic/Method.dart'; import 'package:pikapika/basic/Method.dart';
import 'package:pikapika/basic/config/ExportRename.dart'; import 'package:pikapika/basic/config/ExportRename.dart';
import 'package:pikapika/screens/DownloadExportToSocketScreen.dart'; import 'package:pikapika/screens/DownloadExportToSocketScreen.dart';
import '../basic/config/ExportPath.dart';
import '../basic/config/IconLoading.dart'; import '../basic/config/IconLoading.dart';
import '../basic/config/IsPro.dart';
import 'components/ContentError.dart'; import 'components/ContentError.dart';
import 'components/ContentLoading.dart'; import 'components/ContentLoading.dart';
import 'components/DownloadInfoCard.dart'; import 'components/DownloadInfoCard.dart';
@ -104,11 +101,20 @@ class _DownloadExportToFileScreenState
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: exportResult != "" ? Text(exportResult) : Container(), child: exportResult != "" ? Text(exportResult) : Container(),
), ),
Container( displayExportPathInfo(),
padding: const EdgeInsets.all(8), Container(height: 15),
child: const Text('TIPS : 选择一个目录'), _exportPkzButton(),
), Container(height: 10),
..._buildExportToFileButtons(), _exportPkiButton(),
Container(height: 10),
_exportHtmlZipButton(),
Container(height: 10),
_exportToHtmlJPEGButton(),
Container(height: 10),
_exportToJPEGSZIPButton(),
Container(height: 10),
_exportToHtmlJPEGNotDownOverButton(),
Container(height: 10),
MaterialButton( MaterialButton(
onPressed: () async { onPressed: () async {
Navigator.of(context).push( Navigator.of(context).push(
@ -123,7 +129,7 @@ class _DownloadExportToFileScreenState
}, },
child: _buildButtonInner('传输到其他设备'), child: _buildButtonInner('传输到其他设备'),
), ),
Container(height: 10), Container(height: 40),
], ],
); );
}, },
@ -131,21 +137,109 @@ class _DownloadExportToFileScreenState
); );
} }
List<Widget> _buildExportToFileButtons() { Widget _exportPkzButton() {
List<Widget> widgets = []; return MaterialButton(
if (Platform.isWindows ||
Platform.isMacOS ||
Platform.isLinux ||
Platform.isAndroid) {
widgets.add(MaterialButton(
onPressed: () async { onPressed: () async {
late String? path; var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出PKZ${showExportPath()}")) {
return;
}
}
try { try {
path = Platform.isIOS setState(() {
? await method.iosGetDocumentDir() exporting = true;
: await chooseFolder(context); });
await method.exportComicDownloadToPkz(
[widget.comicId],
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) { } catch (e) {
defaultToast(context, "$e"); setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner(
'导出到xxx.pkz\n(可直接打开观看的格式,不支持导入)\n(可以躲避网盘或者聊天软件的扫描)',
),
);
}
Widget _exportPkiButton() {
return MaterialButton(
onPressed: () async {
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出PKI${showExportPath()}")) {
return;
}
}
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPki(
widget.comicId,
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner(
'导出到xxx.pki\n(只支持导入, 不支持直接阅读)\n(可以躲避网盘或者聊天软件的扫描)\n(后期版本可能支持直接阅读)',
),
);
}
Widget _exportHtmlZipButton() {
return MaterialButton(
onPressed: () async {
if (!isPro) {
defaultToast(context, "请先发电鸭");
return; return;
} }
var name = ""; var name = "";
@ -160,16 +254,72 @@ class _DownloadExportToFileScreenState
} else { } else {
return; return;
} }
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出HTML+ZIP${showExportPath()}")) {
return;
}
}
try {
setState(() {
exporting = true;
});
await method.exportComicDownload(
widget.comicId,
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner(
'导出到HTML.zip\n(可从其他设备导入 / 解压后可阅读)' + (!isPro ? "\n(发电后使用)" : ""),
),
);
}
Widget _exportToHtmlJPEGButton() {
return MaterialButton(
onPressed: () async {
if (!isPro) {
defaultToast(context, "请先发电鸭");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出HTML+JPEG${showExportPath()}")) {
return;
}
} }
print("path $path");
if (path != null) {
try { try {
setState(() { setState(() {
exporting = true; exporting = true;
}); });
await method.exportComicDownloadToJPG( await method.exportComicDownloadToJPG(
widget.comicId, widget.comicId,
path, await attachExportPath(),
name, name,
); );
setState(() { setState(() {
@ -184,290 +334,16 @@ class _DownloadExportToFileScreenState
exporting = false; exporting = false;
}); });
} }
}
}, },
child: _buildButtonInner('导出到HTML+JPG\n(可直接在相册中打开观看)'), child: _buildButtonInner('导出到HTML+JPG\n(可直接在相册中打开观看)'),
));
widgets.add(Container(height: 10));
/////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
); );
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPkz(
[widget.comicId],
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child:
_buildButtonInner('导出到xxx.pkz\n(可直接打开观看的格式,不支持导入)\n(可以躲避网盘或者聊天软件的扫描)'),
));
widgets.add(Container(height: 10));
/////////////////////
/////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPki(
widget.comicId,
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child:
_buildButtonInner('导出到xxx.pki\n(只支持导入, 不支持直接阅读)\n(可以躲避网盘或者聊天软件的扫描)\n(后期版本可能支持直接阅读)'),
));
widgets.add(Container(height: 10));
/////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownload(
widget.comicId,
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child: _buildButtonInner('导出到HTML.zip\n(可从其他设备导入 / 解压后可阅读)'),
));
widgets.add(Container(height: 10));
//////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicJpegsEvenNotFinish(
widget.comicId,
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child: _buildButtonInner('导出到HTML+JPG\n(即使没有下载成功)'),
));
widgets.add(Container(height: 10));
}
if (Platform.isIOS || Platform.isAndroid) {
widgets.add(MaterialButton(
onPressed: () async {
if (!(await confirmDialog(context, "导出确认", "将本漫画所有图片到相册?"))) {
return;
}
if (!(await Permission.storage.request()).isGranted) {
return;
}
try {
setState(() {
exporting = true;
});
//
var count = 0;
List<DownloadEp> eps = await method.downloadEpList(widget.comicId);
for (var i = 0; i < eps.length; i++) {
var pics = await method.downloadPicturesByEpId(eps[i].id);
for (var j = 0; j < pics.length; j++) {
setState(() {
exportMessage = "导出图片 ${count++}";
});
await saveImageQuiet(
await method.downloadImagePath(pics[j].localPath),
context,
);
}
}
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner('将所有图片导出到手机相册'),
));
widgets.add(Container(height: 10));
}
return widgets;
} }
List<Widget> _buildExportToJpegZipButtons() { Widget _exportToJPEGSZIPButton() {
List<Widget> widgets = []; return MaterialButton(
if (Platform.isWindows ||
Platform.isMacOS ||
Platform.isLinux ||
Platform.isAndroid) {
widgets.add(MaterialButton(
onPressed: () async { onPressed: () async {
late String? path; if (!isPro) {
try { defaultToast(context, "请先发电鸭");
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return; return;
} }
var name = ""; var name = "";
@ -482,16 +358,19 @@ class _DownloadExportToFileScreenState
} else { } else {
return; return;
} }
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出JPEGS.zip${showExportPath()}")) {
return;
}
} }
print("path $path");
if (path != null) {
try { try {
setState(() { setState(() {
exporting = true; exporting = true;
}); });
await method.exportComicDownloadJpegZip( await method.exportComicDownloadJpegZip(
widget.comicId, widget.comicId,
path, await attachExportPath(),
name, name,
); );
setState(() { setState(() {
@ -506,21 +385,18 @@ class _DownloadExportToFileScreenState
exporting = false; exporting = false;
}); });
} }
}
}, },
child: _buildButtonInner('导出阅读器用JPGS.zip\n(不可再导入)'), child: _buildButtonInner(
)); '导出阅读器用JPGS.zip\n(不可再导入)' + (!isPro ? "\n(发电后使用)" : ""),
widgets.add(Container(height: 10)); ),
///////////////////// );
widgets.add(MaterialButton( }
Widget _exportToHtmlJPEGNotDownOverButton() {
return MaterialButton(
onPressed: () async { onPressed: () async {
late String? path; if (!isPro) {
try { defaultToast(context, "请先发电鸭");
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return; return;
} }
var name = ""; var name = "";
@ -535,178 +411,19 @@ class _DownloadExportToFileScreenState
} else { } else {
return; return;
} }
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPkz(
[widget.comicId],
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child:
_buildButtonInner('导出到xxx.pkz\n(可直接打开观看的格式,不支持导入)\n(可以躲避网盘或者聊天软件的扫描)'),
));
widgets.add(Container(height: 10));
/////////////////////
/////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else { } else {
if (!await confirmDialog(context, "导出确认",
"将您所选的漫画导出HTML+JPEGS(即使没有下载完成)${showExportPath()}")) {
return; return;
} }
} }
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPki(
widget.comicId,
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child:
_buildButtonInner('导出到xxx.pki\n(只支持导入, 不支持直接阅读)\n(可以躲避网盘或者聊天软件的扫描)\n(后期版本可能支持直接阅读)'),
));
widgets.add(Container(height: 10));
/////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownload(
widget.comicId,
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child: _buildButtonInner('导出到HTML.zip\n(可从其他设备导入 / 解压后可阅读)'),
));
widgets.add(Container(height: 10));
//////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
return;
}
}
print("path $path");
if (path != null) {
try { try {
setState(() { setState(() {
exporting = true; exporting = true;
}); });
await method.exportComicJpegsEvenNotFinish( await method.exportComicJpegsEvenNotFinish(
widget.comicId, widget.comicId,
path, await attachExportPath(),
name, name,
); );
setState(() { setState(() {
@ -721,66 +438,19 @@ class _DownloadExportToFileScreenState
exporting = false; exporting = false;
}); });
} }
}
}, },
child: _buildButtonInner('导出到HTML+JPG\n(即使没有下载成功)'), child: _buildButtonInner(
)); '导出到HTML+JPG\n(即使没有下载成功)' + (!isPro ? "\n(发电后使用)" : ""),
widgets.add(Container(height: 10)); ),
}
if (Platform.isIOS || Platform.isAndroid) {
widgets.add(MaterialButton(
onPressed: () async {
if (!(await confirmDialog(context, "导出确认", "将本漫画所有图片到相册?"))) {
return;
}
if (!(await Permission.storage.request()).isGranted) {
return;
}
try {
setState(() {
exporting = true;
});
//
var count = 0;
List<DownloadEp> eps = await method.downloadEpList(widget.comicId);
for (var i = 0; i < eps.length; i++) {
var pics = await method.downloadPicturesByEpId(eps[i].id);
for (var j = 0; j < pics.length; j++) {
setState(() {
exportMessage = "导出图片 ${count++}";
});
await saveImageQuiet(
await method.downloadImagePath(pics[j].localPath),
context,
); );
} }
}
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner('将所有图片导出到手机相册'),
));
widgets.add(Container(height: 10));
}
return widgets;
}
Widget _buildButtonInner(String text) { Widget _buildButtonInner(String text) {
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
return Container( return Container(
width: constraints.maxWidth, width: constraints.maxWidth,
padding: const EdgeInsets.only(top: 15, bottom: 15), padding: const EdgeInsets.all(15),
color: (Theme.of(context).textTheme.bodyText1?.color ?? Colors.black) color: (Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
.withOpacity(.05), .withOpacity(.05),
child: Text( child: Text(

View File

@ -6,6 +6,7 @@ import 'package:pikapika/basic/Common.dart';
import '../basic/Channels.dart'; import '../basic/Channels.dart';
import '../basic/Cross.dart'; import '../basic/Cross.dart';
import '../basic/Method.dart'; import '../basic/Method.dart';
import '../basic/config/ExportPath.dart';
import '../basic/config/ExportRename.dart'; import '../basic/config/ExportRename.dart';
import '../basic/config/IsPro.dart'; import '../basic/config/IsPro.dart';
import 'components/ContentLoading.dart'; import 'components/ContentLoading.dart';
@ -58,20 +59,39 @@ class _DownloadExportingGroupScreenState
} }
return ListView( return ListView(
children: [ children: [
Container(height: 20),
displayExportPathInfo(),
Container(height: 20), Container(height: 20),
MaterialButton( MaterialButton(
onPressed: _exportPkz, onPressed: _exportPkz,
child: const Text("导出PKZ"), child: _buildButtonInner("导出成一个PKZ\n(加密模式,防止网盘检测,能用pikapika打开观看)"),
), ),
Container(height: 20), Container(height: 20),
MaterialButton( MaterialButton(
onPressed: _exportPkis, onPressed: _exportPkis,
child: Text("分别导出PKI" + (!isPro ? "\n(发电后使用)" : "")), child: _buildButtonInner("每部漫画都打包一个PKI\n(加密模式,防止网盘检测,能用pikapika导入)"),
), ),
Container(height: 20), Container(height: 20),
MaterialButton( MaterialButton(
onPressed: _exportZips, onPressed: _exportZips,
child: Text("分别导出ZIP" + (!isPro ? "\n(发电后使用)" : "")), child: _buildButtonInner(
"每部漫画都打包一个ZIP\n(不加密模式,能用pikapika导入或网页浏览器观看)" +
(!isPro ? "\n(发电后使用)" : "")),
),
Container(height: 20),
MaterialButton(
onPressed: _exportToJPEGSZips,
child: _buildButtonInner(
"每部漫画都打包一个ZIP+JPEG\n(能直接使用其他阅读器看,不可再导入)" +
(!isPro ? "\n(发电后使用)" : ""),
),
),
Container(height: 20),
MaterialButton(
onPressed: _exportToJPEGSFolders,
child: _buildButtonInner(
"每部漫画都导出成文件夹+JPEG" + (!isPro ? "\n(发电后使用)" : ""),
),
), ),
Container(height: 20), Container(height: 20),
], ],
@ -79,15 +99,6 @@ class _DownloadExportingGroupScreenState
} }
_exportPkz() async { _exportPkz() async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
var name = ""; var name = "";
if (currentExportRename()) { if (currentExportRename()) {
var rename = await inputString( var rename = await inputString(
@ -100,16 +111,19 @@ class _DownloadExportingGroupScreenState
} else { } else {
return; return;
} }
} else {
if (!await confirmDialog(
context, "导出确认", "将导出您所选的漫画为一个PKZ${showExportPath()}")) {
return;
}
} }
print("path $path");
if (path != null) {
try { try {
setState(() { setState(() {
exporting = true; exporting = true;
}); });
await method.exportComicDownloadToPkz( await method.exportComicDownloadToPkz(
widget.idList, widget.idList,
path, await attachExportPath(),
name, name,
); );
exported = true; exported = true;
@ -122,31 +136,19 @@ class _DownloadExportingGroupScreenState
}); });
} }
} }
}
_exportPkis() async { _exportPkis() async {
if (!isPro) { if (!await confirmDialog(
defaultToast(context, "请先发电鸭"); context, "导出确认", "将您所选的漫画分别导出成单独的PKI${showExportPath()}")) {
return; return;
} }
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return;
}
print("path $path");
if (path != null) {
try { try {
setState(() { setState(() {
exporting = true; exporting = true;
}); });
await method.exportAnyComicDownloadsToPki( await method.exportAnyComicDownloadsToPki(
widget.idList, widget.idList,
path, await attachExportPath(),
); );
exported = true; exported = true;
} catch (err) { } catch (err) {
@ -158,31 +160,23 @@ class _DownloadExportingGroupScreenState
}); });
} }
} }
}
_exportZips() async { _exportZips() async {
if (!isPro) { if (!isPro) {
defaultToast(context, "请先发电鸭"); defaultToast(context, "请先发电鸭");
return; return;
} }
late String? path; if (!await confirmDialog(
try { context, "导出确认", "将导出您所选的漫画分别导出ZIP${showExportPath()}")) {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
return; return;
} }
print("path $path");
if (path != null) {
try { try {
setState(() { setState(() {
exporting = true; exporting = true;
}); });
await method.exportAnyComicDownloadsToZip( await method.exportAnyComicDownloadsToZip(
widget.idList, widget.idList,
path, await attachExportPath(),
); );
exported = true; exported = true;
} catch (err) { } catch (err) {
@ -194,6 +188,69 @@ class _DownloadExportingGroupScreenState
}); });
} }
} }
_exportToJPEGSZips() async {
if (!isPro) {
defaultToast(context, "请先发电鸭");
return;
}
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画分别导出ZIP+JPEG${showExportPath()}")) {
return;
}
try {
setState(() {
exporting = true;
});
final path = await attachExportPath();
for (var id in widget.idList) {
await method.exportComicDownloadJpegZip(
id,
path,
"",
);
}
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
}
}
_exportToJPEGSFolders() async {
if (!isPro) {
defaultToast(context, "请先发电鸭");
return;
}
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画分别导出文件夹+JPEG${showExportPath()}")) {
return;
}
try {
setState(() {
exporting = true;
});
final path = await attachExportPath();
for (var id in widget.idList) {
await method.exportComicDownloadToJPG(
id,
path,
"",
);
}
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
}
} }
@override @override
@ -214,4 +271,21 @@ class _DownloadExportingGroupScreenState
}, },
); );
} }
Widget _buildButtonInner(String text) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth,
padding: const EdgeInsets.all(15),
color: (Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
.withOpacity(.05),
child: Text(
text,
textAlign: TextAlign.center,
),
);
},
);
}
} }

View File

@ -10,6 +10,7 @@ import 'package:pikapika/basic/config/ChooserRoot.dart';
import '../basic/Cross.dart'; import '../basic/Cross.dart';
import '../basic/config/IconLoading.dart'; import '../basic/config/IconLoading.dart';
import '../basic/config/ImportNotice.dart';
import '../basic/config/IsPro.dart'; import '../basic/config/IsPro.dart';
import 'PkzArchiveScreen.dart'; import 'PkzArchiveScreen.dart';
import 'components/ContentLoading.dart'; import 'components/ContentLoading.dart';
@ -63,12 +64,6 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
); );
} }
List<Widget> actions = [];
actions.add(_fileImportButton());
actions.add(_networkImportButton());
actions.add(_importDirFilesZipButton());
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('导入'), title: const Text('导入'),
@ -79,7 +74,15 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Text(_importMessage), child: Text(_importMessage),
), ),
...actions, Container(height: 20),
importNotice(context),
Container(height: 20),
_fileImportButton(),
Container(height: 20),
_networkImportButton(),
Container(height: 20),
_importDirFilesZipButton(),
Container(height: 40),
], ],
), ),
); );
@ -151,12 +154,22 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
} }
} }
}, },
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth,
padding: const EdgeInsets.only(top: 15, bottom: 15),
color:
(Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
.withOpacity(.05),
child: const Text( child: const Text(
'选择zip文件进行导入\n选择pki文件进行导入\n选择pkz文件进行阅读', '选择zip文件进行导入\n选择pki文件进行导入\n选择pkz文件进行阅读',
style: TextStyle(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
); );
},
),
);
} }
Widget _networkImportButton() { Widget _networkImportButton() {
@ -185,7 +198,21 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
} }
} }
}, },
child: const Text('从其他设备导入'), child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth,
padding: const EdgeInsets.only(top: 15, bottom: 15),
color:
(Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
.withOpacity(.05),
child: const Text(
'从其他设备导入',
textAlign: TextAlign.center,
),
);
},
),
); );
} }
@ -220,11 +247,21 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
} }
} }
}, },
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth,
padding: const EdgeInsets.only(top: 15, bottom: 15),
color:
(Theme.of(context).textTheme.bodyText1?.color ?? Colors.black)
.withOpacity(.05),
child: Text( child: Text(
'选择文件夹\n(导入里面所有的zip/pki)' + (!isPro ? "\n(发电后使用)" : ""), '选择文件夹\n(导入里面所有的zip/pki)' + (!isPro ? "\n(发电后使用)" : ""),
style: TextStyle(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
); );
},
),
);
} }
} }

View File

@ -30,11 +30,9 @@ class _DownloadListScreenState extends State<DownloadListScreen> {
inBar: false, inBar: false,
setState: setState, setState: setState,
onSubmitted: (value) { onSubmitted: (value) {
if (value.isNotEmpty) {
_search = value; _search = value;
_f = method.allDownloads(_search); _f = method.allDownloads(_search);
_searchBar.controller.text = value; _searchBar.controller.text = value;
}
}, },
buildDefaultAppBar: (BuildContext context) { buildDefaultAppBar: (BuildContext context) {
return AppBar( return AppBar(

View File

@ -42,6 +42,7 @@ import 'package:pikapika/screens/PkzArchiveScreen.dart';
import 'package:uni_links/uni_links.dart'; import 'package:uni_links/uni_links.dart';
import 'package:uri_to_file/uri_to_file.dart'; import 'package:uri_to_file/uri_to_file.dart';
import '../basic/config/DownloadCachePath.dart'; import '../basic/config/DownloadCachePath.dart';
import '../basic/config/ExportPath.dart';
import '../basic/config/ExportRename.dart'; import '../basic/config/ExportRename.dart';
import '../basic/config/IconLoading.dart'; import '../basic/config/IconLoading.dart';
import '../basic/config/IsPro.dart'; import '../basic/config/IsPro.dart';
@ -94,6 +95,7 @@ class _InitScreenState extends State<InitScreen> {
await initKeyboardController(); await initKeyboardController();
await initAndroidDisplayMode(); await initAndroidDisplayMode();
await initChooserRoot(); await initChooserRoot();
await initExportPath();
await initTimeZone(); await initTimeZone();
await initDownloadAndExportPath(); await initDownloadAndExportPath();
await initAndroidSecureFlag(); await initAndroidSecureFlag();

View File

@ -906,7 +906,7 @@ class _SettingPanelState extends State<_SettingPanel> {
icon: Icons.shuffle, icon: Icons.shuffle,
title: currentAddressName(), title: currentAddressName(),
onPressed: () async { onPressed: () async {
await chooseAddress(context); await chooseAddressAndSwitch(context);
setState(() {}); setState(() {});
}, },
), ),

View File

@ -273,10 +273,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: "50e882fe0a06bf0c8f7f5bce78d30975f279213293afc9471dc35f05617c50ff" sha256: d4cb8ab04f770dab9d04c7959e5f6d22e8c5280343d425f9344f93832cf58445
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.7+1" version: "0.8.7+2"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.6.8+15 version: 1.7.0+16
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"