♻️ 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")!!)
"verifyAuthentication" -> auth()
"androidStorageRoot" -> storageRoot()
"androidDefaultExportsDir" -> androidDefaultExportsDir().absolutePath
"androidMkdirs" -> androidMkdirs(
call.arguments<String>() ?: throw Exception("need arg")
)
else -> {
notImplementedToken
}
@ -408,4 +412,24 @@ class MainActivity : FlutterActivity() {
fun storageRoot(): String {
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
- [x] ✨选择API分流以及图片分流时进行测速

View File

@ -993,4 +993,20 @@ class Method {
Future 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] ?? "";
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() {
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
@ -67,7 +38,7 @@ Widget switchAddressSetting() {
title: const Text("分流"),
subtitle: Text(currentAddressName()),
onTap: () async {
await chooseAddress(context);
await chooseAddressAndSwitch(context);
setState(() {});
},
);
@ -118,8 +89,12 @@ Future chooseAddressAndSwitch(BuildContext context) async {
title: const Text('选择分流'),
children: <Widget>[
..._addresses.entries.map(
(e) => SimpleDialogOption(
child: Text(e.value),
(e) => SimpleDialogOption(
child: ApiOptionRow(
e.value,
e.key,
key: Key("API:${e.key}"),
),
onPressed: () {
Navigator.of(context).pop(e.key);
},

View File

@ -10,30 +10,22 @@ import '../Method.dart';
const _propertyName = "chooserRoot";
late String _chooserRoot;
late String _androidDefaultRoot;
Future<dynamic> initChooserRoot() async {
_chooserRoot = await method.loadProperty(_propertyName, "");
if (Platform.isAndroid) {
_androidDefaultRoot = await method.androidStorageRoot();
}
}
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 '';
if (_chooserRoot.isEmpty) {
if (Platform.isAndroid) {
try {
_chooserRoot = await method.androidStorageRoot();
} catch (e) {
_chooserRoot = "/sdcard";
}
} else if (Platform.isMacOS || Platform.isLinux) {
_chooserRoot = await method.getHomeDir();
} else if (Platform.isWindows) {
_chooserRoot = "/";
}
}
return _chooserRoot;
}
Future<String> currentChooserRoot() async {
@ -42,7 +34,7 @@ Future<String> currentChooserRoot() async {
throw Exception("申请权限被拒绝");
}
}
return _currentChooserRoot();
return _chooserRoot;
}
Future<dynamic> _inputChooserRoot(BuildContext context) async {
@ -67,7 +59,7 @@ Widget chooserRootSetting() {
builder: (BuildContext context, void Function(void Function()) setState) {
return ListTile(
title: const Text("文件夹选择器默认路径"),
subtitle: Text(_currentChooserRoot()),
subtitle: Text(_chooserRoot),
onTap: () async {
await _inputChooserRoot(context);
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:io';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:pikapika/basic/Channels.dart';
import 'package:pikapika/basic/Common.dart';
import 'package:pikapika/basic/Cross.dart';
import 'package:pikapika/basic/Entities.dart';
import 'package:pikapika/basic/Method.dart';
import 'package:pikapika/basic/config/ExportRename.dart';
import 'package:pikapika/screens/DownloadExportToSocketScreen.dart';
import '../basic/config/ExportPath.dart';
import '../basic/config/IconLoading.dart';
import '../basic/config/IsPro.dart';
import 'components/ContentError.dart';
import 'components/ContentLoading.dart';
import 'components/DownloadInfoCard.dart';
@ -104,11 +101,20 @@ class _DownloadExportToFileScreenState
padding: const EdgeInsets.all(8),
child: exportResult != "" ? Text(exportResult) : Container(),
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('TIPS : 选择一个目录'),
),
..._buildExportToFileButtons(),
displayExportPathInfo(),
Container(height: 15),
_exportPkzButton(),
Container(height: 10),
_exportPkiButton(),
Container(height: 10),
_exportHtmlZipButton(),
Container(height: 10),
_exportToHtmlJPEGButton(),
Container(height: 10),
_exportToJPEGSZIPButton(),
Container(height: 10),
_exportToHtmlJPEGNotDownOverButton(),
Container(height: 10),
MaterialButton(
onPressed: () async {
Navigator.of(context).push(
@ -123,7 +129,7 @@ class _DownloadExportToFileScreenState
},
child: _buildButtonInner('传输到其他设备'),
),
Container(height: 10),
Container(height: 40),
],
);
},
@ -131,648 +137,312 @@ class _DownloadExportToFileScreenState
);
}
List<Widget> _buildExportToFileButtons() {
List<Widget> widgets = [];
if (Platform.isWindows ||
Platform.isMacOS ||
Platform.isLinux ||
Platform.isAndroid) {
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
Widget _exportPkzButton() {
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;
}
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.exportComicDownloadToJPG(
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));
/////////////////////
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出PKZ${showExportPath()}")) {
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;
}
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPkz(
[widget.comicId],
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner(
'导出到xxx.pkz\n(可直接打开观看的格式,不支持导入)\n(可以躲避网盘或者聊天软件的扫描)',
),
);
}
List<Widget> _buildExportToJpegZipButtons() {
List<Widget> widgets = [];
if (Platform.isWindows ||
Platform.isMacOS ||
Platform.isLinux ||
Platform.isAndroid) {
widgets.add(MaterialButton(
onPressed: () async {
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
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;
}
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.exportComicDownloadJpegZip(
widget.comicId,
path,
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
}
},
child: _buildButtonInner('导出阅读器用JPGS.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");
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出PKI${showExportPath()}")) {
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");
}
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;
}
var name = "";
if (currentExportRename()) {
var rename = await inputString(
context,
"请输入保存后的名称",
defaultValue: _task.title,
);
if (rename != null && rename.isNotEmpty) {
name = rename;
} else {
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");
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出HTML+ZIP${showExportPath()}")) {
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");
}
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;
}
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, "导出确认", "将本漫画所有图片到相册?"))) {
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出HTML+JPEG${showExportPath()}")) {
return;
}
if (!(await Permission.storage.request()).isGranted) {
}
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToJPG(
widget.comicId,
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner('导出到HTML+JPG\n(可直接在相册中打开观看)'),
);
}
Widget _exportToJPEGSZIPButton() {
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;
}
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;
});
} else {
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画导出JPEGS.zip${showExportPath()}")) {
return;
}
},
child: _buildButtonInner('将所有图片导出到手机相册'),
));
widgets.add(Container(height: 10));
}
return widgets;
}
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadJpegZip(
widget.comicId,
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner(
'导出阅读器用JPGS.zip\n(不可再导入)' + (!isPro ? "\n(发电后使用)" : ""),
),
);
}
Widget _exportToHtmlJPEGNotDownOverButton() {
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+JPEGS(即使没有下载完成)${showExportPath()}")) {
return;
}
}
try {
setState(() {
exporting = true;
});
await method.exportComicJpegsEvenNotFinish(
widget.comicId,
await attachExportPath(),
name,
);
setState(() {
exportResult = "导出成功";
});
} catch (e) {
setState(() {
exportResult = "导出失败 $e";
});
} finally {
setState(() {
exporting = false;
});
}
},
child: _buildButtonInner(
'导出到HTML+JPG\n(即使没有下载成功)' + (!isPro ? "\n(发电后使用)" : ""),
),
);
}
Widget _buildButtonInner(String text) {
@ -780,7 +450,7 @@ class _DownloadExportToFileScreenState
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
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)
.withOpacity(.05),
child: Text(

View File

@ -6,6 +6,7 @@ import 'package:pikapika/basic/Common.dart';
import '../basic/Channels.dart';
import '../basic/Cross.dart';
import '../basic/Method.dart';
import '../basic/config/ExportPath.dart';
import '../basic/config/ExportRename.dart';
import '../basic/config/IsPro.dart';
import 'components/ContentLoading.dart';
@ -58,20 +59,39 @@ class _DownloadExportingGroupScreenState
}
return ListView(
children: [
Container(height: 20),
displayExportPathInfo(),
Container(height: 20),
MaterialButton(
onPressed: _exportPkz,
child: const Text("导出PKZ"),
child: _buildButtonInner("导出成一个PKZ\n(加密模式,防止网盘检测,能用pikapika打开观看)"),
),
Container(height: 20),
MaterialButton(
onPressed: _exportPkis,
child: Text("分别导出PKI" + (!isPro ? "\n(发电后使用)" : "")),
child: _buildButtonInner("每部漫画都打包一个PKI\n(加密模式,防止网盘检测,能用pikapika导入)"),
),
Container(height: 20),
MaterialButton(
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),
],
@ -79,15 +99,6 @@ class _DownloadExportingGroupScreenState
}
_exportPkz() 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(
@ -100,63 +111,53 @@ class _DownloadExportingGroupScreenState
} else {
return;
}
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPkz(
widget.idList,
path,
name,
);
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
} else {
if (!await confirmDialog(
context, "导出确认", "将导出您所选的漫画为一个PKZ${showExportPath()}")) {
return;
}
}
try {
setState(() {
exporting = true;
});
await method.exportComicDownloadToPkz(
widget.idList,
await attachExportPath(),
name,
);
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
}
}
_exportPkis() async {
if (!isPro) {
defaultToast(context, "请先发电鸭");
if (!await confirmDialog(
context, "导出确认", "将您所选的漫画分别导出成单独的PKI${showExportPath()}")) {
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 {
setState(() {
exporting = true;
});
await method.exportAnyComicDownloadsToPki(
widget.idList,
path,
);
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
}
setState(() {
exporting = true;
});
await method.exportAnyComicDownloadsToPki(
widget.idList,
await attachExportPath(),
);
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
}
}
@ -165,34 +166,90 @@ class _DownloadExportingGroupScreenState
defaultToast(context, "请先发电鸭");
return;
}
late String? path;
try {
path = Platform.isIOS
? await method.iosGetDocumentDir()
: await chooseFolder(context);
} catch (e) {
defaultToast(context, "$e");
if (!await confirmDialog(
context, "导出确认", "将导出您所选的漫画分别导出ZIP${showExportPath()}")) {
return;
}
print("path $path");
if (path != null) {
try {
setState(() {
exporting = true;
});
await method.exportAnyComicDownloadsToZip(
widget.idList,
try {
setState(() {
exporting = true;
});
await method.exportAnyComicDownloadsToZip(
widget.idList,
await attachExportPath(),
);
exported = true;
} catch (err) {
e = err;
exportFail = true;
} finally {
setState(() {
exporting = false;
});
}
}
_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;
});
}
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;
});
}
}
@ -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/config/IconLoading.dart';
import '../basic/config/ImportNotice.dart';
import '../basic/config/IsPro.dart';
import 'PkzArchiveScreen.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(
appBar: AppBar(
title: const Text('导入'),
@ -79,7 +74,15 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
padding: const EdgeInsets.all(10),
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,10 +154,20 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
}
}
},
child: const Text(
'选择zip文件进行导入\n选择pki文件进行导入\n选择pkz文件进行阅读',
style: TextStyle(),
textAlign: TextAlign.center,
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(
'选择zip文件进行导入\n选择pki文件进行导入\n选择pkz文件进行阅读',
textAlign: TextAlign.center,
),
);
},
),
);
}
@ -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,10 +247,20 @@ class _DownloadImportScreenState extends State<DownloadImportScreen> {
}
}
},
child: Text(
'选择文件夹\n(导入里面所有的zip/pki)' + (!isPro ? "\n(发电后使用)" : ""),
style: TextStyle(),
textAlign: TextAlign.center,
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(导入里面所有的zip/pki)' + (!isPro ? "\n(发电后使用)" : ""),
textAlign: TextAlign.center,
),
);
},
),
);
}

View File

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

View File

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

View File

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

View File

@ -273,10 +273,10 @@ packages:
dependency: transitive
description:
name: image_picker_ios
sha256: "50e882fe0a06bf0c8f7f5bce78d30975f279213293afc9471dc35f05617c50ff"
sha256: d4cb8ab04f770dab9d04c7959e5f6d22e8c5280343d425f9344f93832cf58445
url: "https://pub.dev"
source: hosted
version: "0.8.7+1"
version: "0.8.7+2"
image_picker_platform_interface:
dependency: transitive
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.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.6.8+15
version: 1.7.0+16
environment:
sdk: ">=2.12.0 <3.0.0"