♻️ Permisson rewrite
This commit is contained in:
parent
2c30885bc3
commit
4a2489dcf4
16
README.md
16
README.md
|
@ -89,21 +89,11 @@ VPN->代理->分流, 这三个功能如果同时设置, 您会在您手机的VPN
|
||||||
|
|
||||||
## 请您遵守使用规则
|
## 请您遵守使用规则
|
||||||
|
|
||||||
本文中提到的本软件拓展包括但是不限于以下内容
|
软件副本分发以及代码使用规则
|
||||||
|
|
||||||
- 使用本软件进行继续开发形成的软件。
|
- 本软件的代码在未经允许的情况下可以自用但不允许释放任何releases, 个人或企业不可用于商业用途, 不可上架任何商店。。
|
||||||
- 引入本软件部分内容为依赖/使用本软件内代码的同时包含本软件内一致内容或功能。
|
|
||||||
- 直接对本软件进行打包发布
|
|
||||||
|
|
||||||
软件副本分发以及代码使用规则规则
|
|
||||||
|
|
||||||
- 本软件仅供学习交流使用, 本软件或本软件的拓展, 个人或企业不可用于商业用途, 不可上架任何商店。
|
|
||||||
- 本软件的拓展在未经允许的情况下可以自用但不允许释放任何releases。
|
|
||||||
- 不要在任何其他 **二次元软件** 的 **聊天社区** 或 **开发社区** 内, 发布有关本软件的链接或信息, 对于观点不同产生的分歧作者不站队任何立场。
|
- 不要在任何其他 **二次元软件** 的 **聊天社区** 或 **开发社区** 内, 发布有关本软件的链接或信息, 对于观点不同产生的分歧作者不站队任何立场。
|
||||||
- 不要发送本软件安装包到 **任何社区内** , 不要将APK/IPA/ZIP/DMG发送包括任何聊天软件内的群聊功能。 分享本软件时, 在社区中使用Github中提供的Releases页面的链接, 或使用私聊窗口发送。
|
- 不要发送本软件安装包到 **任何社区内** , 不要将APK/IPA/ZIP/DMG发送包括任何聊天软件内的群聊功能。 请使用Github中提供的Releases页面的链接。
|
||||||
|
|
||||||
源代码使用规则
|
|
||||||
|
|
||||||
- 对本仓库的fork需要保留本仓库的链接, 以引导用户在主要仓库进行讨论。
|
- 对本仓库的fork需要保留本仓库的链接, 以引导用户在主要仓库进行讨论。
|
||||||
|
|
||||||
责任声明
|
责任声明
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
v1.7.2
|
v1.7.2
|
||||||
- [x] 🐛 修复安卓13导入导出的问题
|
- [x] 🐛 修复安卓13导入导出的问题
|
||||||
- [x] 🐛 修复测速不好用的问题
|
- [x] 🐛 修复测速不好用的问题
|
||||||
|
- [x] ♻️ 梳理一些权限
|
||||||
|
- [x] ✨ 增加批量下载功能
|
||||||
|
- [x] ✨ 增加PAT入会发电
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
|
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
|
||||||
|
import 'package:pikapika/screens/AccessKeyReplaceScreen.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';
|
||||||
|
|
||||||
|
@ -300,7 +301,17 @@ StreamSubscription<String?> linkSubscript(BuildContext context) {
|
||||||
return linkStream.listen((uri) async {
|
return linkStream.listen((uri) async {
|
||||||
if (uri == null) return;
|
if (uri == null) return;
|
||||||
var parsed = Uri.parse(uri);
|
var parsed = Uri.parse(uri);
|
||||||
if (RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(uri).isNotEmpty) {
|
if (RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$").allMatches(uri).isNotEmpty) {
|
||||||
|
String accessKey = RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$")
|
||||||
|
.allMatches(uri)
|
||||||
|
.first
|
||||||
|
.group(1)!;
|
||||||
|
Navigator.of(context).push(
|
||||||
|
mixRoute(
|
||||||
|
builder: (BuildContext context) => AccessKeyReplaceScreen(accessKey: accessKey),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(uri).isNotEmpty) {
|
||||||
String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$")
|
String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$")
|
||||||
.allMatches(uri)
|
.allMatches(uri)
|
||||||
.first
|
.first
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:pikapika/basic/Common.dart';
|
import 'package:pikapika/basic/Common.dart';
|
||||||
|
import 'package:pikapika/basic/config/Platform.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'Method.dart';
|
import 'Method.dart';
|
||||||
|
@ -68,8 +69,13 @@ Future<dynamic> saveImageQuiet(String path, BuildContext context) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> _saveImageAndroid(String path, BuildContext context) async {
|
Future<dynamic> _saveImageAndroid(String path, BuildContext context) async {
|
||||||
var p = await Permission.storage.request();
|
late bool g;
|
||||||
if (!p.isGranted) {
|
if (androidVersion < 30) {
|
||||||
|
g = await Permission.storage.request().isGranted;
|
||||||
|
}else{
|
||||||
|
g = await Permission.manageExternalStorage.request().isGranted;
|
||||||
|
}
|
||||||
|
if (!g) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return method.androidSaveFileToImage(path);
|
return method.androidSaveFileToImage(path);
|
||||||
|
|
|
@ -1065,6 +1065,7 @@ class ProInfoPat {
|
||||||
required this.reBind,
|
required this.reBind,
|
||||||
required this.errorType,
|
required this.errorType,
|
||||||
required this.errorMsg,
|
required this.errorMsg,
|
||||||
|
required this.accessKey,
|
||||||
});
|
});
|
||||||
late final bool isPro;
|
late final bool isPro;
|
||||||
late final String patId;
|
late final String patId;
|
||||||
|
@ -1073,6 +1074,7 @@ class ProInfoPat {
|
||||||
late final int reBind;
|
late final int reBind;
|
||||||
late final int errorType;
|
late final int errorType;
|
||||||
late final String errorMsg;
|
late final String errorMsg;
|
||||||
|
late final String accessKey;
|
||||||
|
|
||||||
ProInfoPat.fromJson(Map<String, dynamic> json){
|
ProInfoPat.fromJson(Map<String, dynamic> json){
|
||||||
isPro = json['is_pro'];
|
isPro = json['is_pro'];
|
||||||
|
@ -1082,6 +1084,7 @@ class ProInfoPat {
|
||||||
reBind = json['re_bind'];
|
reBind = json['re_bind'];
|
||||||
errorType = json['error_type'];
|
errorType = json['error_type'];
|
||||||
errorMsg = json['error_msg'];
|
errorMsg = json['error_msg'];
|
||||||
|
accessKey = json['access_key'];
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -1093,6 +1096,7 @@ class ProInfoPat {
|
||||||
_data['re_bind'] = reBind;
|
_data['re_bind'] = reBind;
|
||||||
_data['error_type'] = errorType;
|
_data['error_type'] = errorType;
|
||||||
_data['error_msg'] = errorMsg;
|
_data['error_msg'] = errorMsg;
|
||||||
|
_data['access_key'] = accessKey;
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1013,4 +1013,20 @@ class Method {
|
||||||
Future downloadAll(List<String> comicIds) {
|
Future downloadAll(List<String> comicIds) {
|
||||||
return _flatInvoke("downloadAll", comicIds);
|
return _flatInvoke("downloadAll", comicIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future setPatAccessKey(String accessKey) {
|
||||||
|
return _flatInvoke("setPatAccessKey", accessKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future reloadPatAccount() {
|
||||||
|
return _flatInvoke("reloadPatAccount", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future bindThisAccount() {
|
||||||
|
return _flatInvoke("bindThisAccount", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future clearPat() {
|
||||||
|
return _flatInvoke("clearPat", "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
import '../Common.dart';
|
import '../Common.dart';
|
||||||
import '../Method.dart';
|
import '../Method.dart';
|
||||||
|
import 'Platform.dart';
|
||||||
|
|
||||||
const _propertyName = "chooserRoot";
|
const _propertyName = "chooserRoot";
|
||||||
late String _chooserRoot;
|
late String _chooserRoot;
|
||||||
|
@ -30,7 +31,13 @@ Future<dynamic> initChooserRoot() async {
|
||||||
|
|
||||||
Future<String> currentChooserRoot() async {
|
Future<String> currentChooserRoot() async {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
if (!(await Permission.storage.request()).isGranted) {
|
late bool g;
|
||||||
|
if (androidVersion < 30) {
|
||||||
|
g = await Permission.storage.request().isGranted;
|
||||||
|
}else{
|
||||||
|
g = await Permission.manageExternalStorage.request().isGranted;
|
||||||
|
}
|
||||||
|
if (!g) {
|
||||||
throw Exception("申请权限被拒绝");
|
throw Exception("申请权限被拒绝");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import '../Cross.dart';
|
import '../Cross.dart';
|
||||||
import '../Method.dart';
|
import '../Method.dart';
|
||||||
|
import 'Platform.dart';
|
||||||
|
|
||||||
const _propertyName = "exportPath";
|
const _propertyName = "exportPath";
|
||||||
late String _exportPath;
|
late String _exportPath;
|
||||||
|
@ -35,7 +36,13 @@ Future<String> attachExportPath() async {
|
||||||
path = await method.iosGetDocumentDir();
|
path = await method.iosGetDocumentDir();
|
||||||
} else {
|
} else {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
if (!(await Permission.storage.request()).isGranted) {
|
late bool g;
|
||||||
|
if (androidVersion < 30) {
|
||||||
|
g = await Permission.storage.request().isGranted;
|
||||||
|
}else{
|
||||||
|
g = await Permission.manageExternalStorage.request().isGranted;
|
||||||
|
}
|
||||||
|
if (!g) {
|
||||||
throw Exception("申请权限被拒绝");
|
throw Exception("申请权限被拒绝");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapika/basic/Method.dart';
|
||||||
|
import 'package:pikapika/screens/components/ContentLoading.dart';
|
||||||
|
|
||||||
|
import '../basic/config/IsPro.dart';
|
||||||
|
|
||||||
|
class AccessKeyReplaceScreen extends StatefulWidget {
|
||||||
|
final String accessKey;
|
||||||
|
|
||||||
|
const AccessKeyReplaceScreen({Key? key, required this.accessKey})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _AccessKeyReplaceScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccessKeyReplaceScreenState extends State<AccessKeyReplaceScreen> {
|
||||||
|
var _loading = false;
|
||||||
|
var _message = "";
|
||||||
|
var _success = false;
|
||||||
|
|
||||||
|
_set() async {
|
||||||
|
setState(() {
|
||||||
|
_loading = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await method.setPatAccessKey(widget.accessKey);
|
||||||
|
await reloadIsPro();
|
||||||
|
_success = true;
|
||||||
|
} catch (e) {
|
||||||
|
_message = "错误 : $e";
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _content() {
|
||||||
|
if (_loading) {
|
||||||
|
return const ContentLoading(label: "加载中");
|
||||||
|
}
|
||||||
|
if (_success) {
|
||||||
|
return const Text("您的赞助登录成功, 请返回");
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Container()),
|
||||||
|
Text(widget.accessKey),
|
||||||
|
Text(_message),
|
||||||
|
Container(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
MaterialButton(
|
||||||
|
color: Colors.grey,
|
||||||
|
onPressed: _set,
|
||||||
|
child: const Text("确认"),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("更换PAT账户"),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: _content(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -160,7 +160,11 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
appBar = AppBar(
|
appBar = AppBar(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
actions: [
|
actions: [
|
||||||
commonPopMenu(context),
|
commonPopMenu(
|
||||||
|
context,
|
||||||
|
setState: setState,
|
||||||
|
comicListController: _comicListController,
|
||||||
|
),
|
||||||
addressPopMenu(context),
|
addressPopMenu(context),
|
||||||
_chooseCategoryAction(),
|
_chooseCategoryAction(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -37,6 +37,7 @@ import 'package:pikapika/basic/config/Version.dart';
|
||||||
import 'package:pikapika/basic/config/VolumeController.dart';
|
import 'package:pikapika/basic/config/VolumeController.dart';
|
||||||
import 'package:pikapika/basic/config/ShadowCategoriesMode.dart';
|
import 'package:pikapika/basic/config/ShadowCategoriesMode.dart';
|
||||||
import 'package:pikapika/basic/config/WillPopNotice.dart';
|
import 'package:pikapika/basic/config/WillPopNotice.dart';
|
||||||
|
import 'package:pikapika/screens/AccessKeyReplaceScreen.dart';
|
||||||
import 'package:pikapika/screens/ComicInfoScreen.dart';
|
import 'package:pikapika/screens/ComicInfoScreen.dart';
|
||||||
import 'package:pikapika/screens/PkzArchiveScreen.dart';
|
import 'package:pikapika/screens/PkzArchiveScreen.dart';
|
||||||
import 'package:uni_links/uni_links.dart';
|
import 'package:uni_links/uni_links.dart';
|
||||||
|
@ -126,15 +127,37 @@ class _InitScreenState extends State<InitScreen> {
|
||||||
}
|
}
|
||||||
if (initUrl != null) {
|
if (initUrl != null) {
|
||||||
var parsed = Uri.parse(initUrl!);
|
var parsed = Uri.parse(initUrl!);
|
||||||
if (RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(initUrl!).isNotEmpty) {
|
if (RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$")
|
||||||
String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$").allMatches(initUrl!).first.group(1)!;
|
.allMatches(initUrl!)
|
||||||
|
.isNotEmpty) {
|
||||||
|
String accessKey = RegExp(r"^pika://access_key/([0-9A-z:\-]+)/$")
|
||||||
|
.allMatches(initUrl!)
|
||||||
|
.first
|
||||||
|
.group(1)!;
|
||||||
|
Navigator.of(context).pushReplacement(mixRoute(
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
AccessKeyReplaceScreen(accessKey: accessKey),
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
} else if (RegExp(r"^pika://comic/([0-9A-z]+)/$")
|
||||||
|
.allMatches(initUrl!)
|
||||||
|
.isNotEmpty) {
|
||||||
|
String comicId = RegExp(r"^pika://comic/([0-9A-z]+)/$")
|
||||||
|
.allMatches(initUrl!)
|
||||||
|
.first
|
||||||
|
.group(1)!;
|
||||||
Navigator.of(context).pushReplacement(mixRoute(
|
Navigator.of(context).pushReplacement(mixRoute(
|
||||||
builder: (BuildContext context) =>
|
builder: (BuildContext context) =>
|
||||||
ComicInfoScreen(comicId: comicId, holdPkz: true),
|
ComicInfoScreen(comicId: comicId, holdPkz: true),
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
} if (RegExp(r"^https?://pika/comic/([0-9A-z]+)/$").allMatches(initUrl!).isNotEmpty) {
|
} else if (RegExp(r"^https?://pika/comic/([0-9A-z]+)/$")
|
||||||
String comicId = RegExp(r"^https?://pika/comic/([0-9A-z]+)/$").allMatches(initUrl!).first.group(1)!;
|
.allMatches(initUrl!)
|
||||||
|
.isNotEmpty) {
|
||||||
|
String comicId = RegExp(r"^https?://pika/comic/([0-9A-z]+)/$")
|
||||||
|
.allMatches(initUrl!)
|
||||||
|
.first
|
||||||
|
.group(1)!;
|
||||||
Navigator.of(context).pushReplacement(mixRoute(
|
Navigator.of(context).pushReplacement(mixRoute(
|
||||||
builder: (BuildContext context) =>
|
builder: (BuildContext context) =>
|
||||||
ComicInfoScreen(comicId: comicId, holdPkz: true),
|
ComicInfoScreen(comicId: comicId, holdPkz: true),
|
||||||
|
@ -147,7 +170,9 @@ class _InitScreenState extends State<InitScreen> {
|
||||||
PkzArchiveScreen(pkzPath: file.path, holdPkz: true),
|
PkzArchiveScreen(pkzPath: file.path, holdPkz: true),
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
} else if (RegExp(r"^.*\.((pki)|(zip))$").allMatches(parsed.path).isNotEmpty) {
|
} else if (RegExp(r"^.*\.((pki)|(zip))$")
|
||||||
|
.allMatches(parsed.path)
|
||||||
|
.isNotEmpty) {
|
||||||
File file = await toFile(initUrl!);
|
File file = await toFile(initUrl!);
|
||||||
Navigator.of(context).pushReplacement(
|
Navigator.of(context).pushReplacement(
|
||||||
mixRoute(
|
mixRoute(
|
||||||
|
|
|
@ -14,6 +14,7 @@ import 'package:uri_to_file/uri_to_file.dart';
|
||||||
import '../basic/Common.dart';
|
import '../basic/Common.dart';
|
||||||
import '../basic/Navigator.dart';
|
import '../basic/Navigator.dart';
|
||||||
import '../basic/config/IconLoading.dart';
|
import '../basic/config/IconLoading.dart';
|
||||||
|
import '../basic/config/Platform.dart';
|
||||||
import 'PkzComicInfoScreen.dart';
|
import 'PkzComicInfoScreen.dart';
|
||||||
|
|
||||||
class PkzArchiveScreen extends StatefulWidget {
|
class PkzArchiveScreen extends StatefulWidget {
|
||||||
|
@ -75,9 +76,16 @@ class _PkzArchiveScreenState extends State<PkzArchiveScreen> with RouteAware {
|
||||||
|
|
||||||
Future _load() async {
|
Future _load() async {
|
||||||
await method.viewPkz(_fileName, widget.pkzPath);
|
await method.viewPkz(_fileName, widget.pkzPath);
|
||||||
var p = await Permission.storage.request();
|
if (Platform.isAndroid) {
|
||||||
if (!p.isGranted) {
|
late bool g;
|
||||||
throw 'error permission';
|
if (androidVersion < 30) {
|
||||||
|
g = await Permission.storage.request().isGranted;
|
||||||
|
}else{
|
||||||
|
g = await Permission.manageExternalStorage.request().isGranted;
|
||||||
|
}
|
||||||
|
if (!g) {
|
||||||
|
throw 'error permission';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_info = await method.pkzInfo(widget.pkzPath);
|
_info = await method.pkzInfo(widget.pkzPath);
|
||||||
if (_info.comics.length == 1) {
|
if (_info.comics.length == 1) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:pikapika/basic/Common.dart';
|
import 'package:pikapika/basic/Common.dart';
|
||||||
import 'package:pikapika/basic/Method.dart';
|
import 'package:pikapika/basic/Method.dart';
|
||||||
|
import 'package:pikapika/screens/AccessKeyReplaceScreen.dart';
|
||||||
|
|
||||||
|
import '../basic/config/IconLoading.dart';
|
||||||
import '../basic/config/IsPro.dart';
|
import '../basic/config/IsPro.dart';
|
||||||
|
|
||||||
class ProScreen extends StatefulWidget {
|
class ProScreen extends StatefulWidget {
|
||||||
|
@ -21,9 +26,20 @@ class _ProScreenState extends State<ProScreen> {
|
||||||
_username = value;
|
_username = value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
proEvent.subscribe(_setState);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
proEvent.unsubscribe(_setState);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_setState(_) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var size = MediaQuery.of(context).size;
|
var size = MediaQuery.of(context).size;
|
||||||
|
@ -51,43 +67,25 @@ class _ProScreenState extends State<ProScreen> {
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
child: Text(
|
child: Text(
|
||||||
"点击\"我曾经发过电\"进同步发电状态\n"
|
"去\"关于\"界面找到维护地址可获得发电指引\n\n"
|
||||||
"点击\"我刚才发了电\"兑换作者给您的礼物卡\n"
|
"1. \"签到/游戏/兑换\" \n"
|
||||||
"去\"关于\"界面找到维护地址用爱发电",
|
" (1). \"我曾经发过电\"可同步相应发电状态\n"
|
||||||
),
|
" (2). \"我刚才发了电\"兑换作者给您的礼物卡\n"
|
||||||
),
|
"\n"
|
||||||
const Divider(),
|
"2. \"PAT入会\"\n"
|
||||||
const Padding(
|
" 🔗将社区账号链接到软件, 同步成员状态, 订阅式发电"
|
||||||
padding: EdgeInsets.all(20),
|
"",
|
||||||
child: Text(
|
|
||||||
"发电小功能 \n"
|
|
||||||
" 多线程下载\n"
|
|
||||||
" 批量导入导出\n"
|
|
||||||
" 跳页",
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("签到或礼物卡"),
|
title: const Text("签到/游戏/兑换"),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
proInfoAf.isPro
|
proInfoAf.isPro
|
||||||
? "发电中 (${DateTime.fromMillisecondsSinceEpoch(1000 * proInfoAf.expire).toString()})"
|
? "发电中 (${DateTime.fromMillisecondsSinceEpoch(1000 * proInfoAf.expire).toString()})"
|
||||||
: "未发电",
|
: "未发电",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...(proInfoPat.patId.isNotEmpty ? [
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
managementPat();
|
|
||||||
},
|
|
||||||
title: const Text("P站支持"),
|
|
||||||
subtitle: Text((
|
|
||||||
proInfoPat.isPro
|
|
||||||
? "发电中"
|
|
||||||
: "未发电") + ("(点击管理)"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] : []),
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("我曾经发过电"),
|
title: const Text("我曾经发过电"),
|
||||||
|
@ -120,12 +118,156 @@ class _ProScreenState extends State<ProScreen> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
...patPro(),
|
||||||
|
const Divider(),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: Text(
|
||||||
|
"发电小功能 \n"
|
||||||
|
" 多线程下载\n"
|
||||||
|
" 批量导入导出\n"
|
||||||
|
" 跳页",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const Divider(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void managementPat() {
|
List<Widget> patPro() {
|
||||||
// todo
|
List<Widget> widgets = [];
|
||||||
|
if (proInfoPat.accessKey.isNotEmpty) {
|
||||||
|
var text = "密钥 : 已录入";
|
||||||
|
if (proInfoPat.patId.isNotEmpty) {
|
||||||
|
text += "\nPAT账号 : ${proInfoPat.patId}";
|
||||||
|
}
|
||||||
|
if (proInfoPat.bindUid.isNotEmpty) {
|
||||||
|
text += "\n绑定PIKA账号 : ${proInfoPat.bindUid}";
|
||||||
|
}
|
||||||
|
if (proInfoPat.requestDelete > 0) {
|
||||||
|
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
proInfoPat.requestDelete * 1000,
|
||||||
|
isUtc: true,
|
||||||
|
);
|
||||||
|
String formattedDate =
|
||||||
|
DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime.toLocal());
|
||||||
|
text += "\n绑定账号时间 : $formattedDate";
|
||||||
|
}
|
||||||
|
if (proInfoPat.reBind > 0) {
|
||||||
|
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
proInfoPat.reBind * 1000,
|
||||||
|
isUtc: true,
|
||||||
|
);
|
||||||
|
String formattedDate =
|
||||||
|
DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime.toLocal());
|
||||||
|
text += "\n可以换绑时间 : $formattedDate";
|
||||||
|
}
|
||||||
|
List<TextSpan> append = [];
|
||||||
|
if (proInfoPat.bindUid == "") {
|
||||||
|
append.add(const TextSpan(
|
||||||
|
text: "\n(请点击这里绑定到当前账号发电)",
|
||||||
|
style: TextStyle(color: Colors.blue),
|
||||||
|
));
|
||||||
|
} else if (proInfoPat.bindUid != _username) {
|
||||||
|
append.add(const TextSpan(
|
||||||
|
text: "\n(请点换绑到当前账号发电)",
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
));
|
||||||
|
} else if (proInfoPat.isPro == false) {
|
||||||
|
append.add(const TextSpan(
|
||||||
|
text: "\n(未检测到入会, 请到下载页入会)",
|
||||||
|
style: TextStyle(color: Colors.orange),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
append.add(const TextSpan(
|
||||||
|
text: "\n(PAT正常)",
|
||||||
|
style: TextStyle(color: Colors.green),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
widgets.add(ListTile(
|
||||||
|
onTap: () async {
|
||||||
|
print(jsonEncode(proInfoPat));
|
||||||
|
var choose = await chooseMapDialog<int>(
|
||||||
|
context,
|
||||||
|
{
|
||||||
|
"更新PAT发电状态": 2,
|
||||||
|
"绑定到此账号": 3,
|
||||||
|
"更换PAT密钥": 1,
|
||||||
|
"清除PAT信息": 4,
|
||||||
|
},
|
||||||
|
"请选择",
|
||||||
|
);
|
||||||
|
switch (choose) {
|
||||||
|
case 1:
|
||||||
|
addPatAccount();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
reloadPatAccount();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
bindThisAccount();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
clearPat();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: const Text("PAT入会"),
|
||||||
|
subtitle: Text.rich(TextSpan(children: [
|
||||||
|
TextSpan(text: text),
|
||||||
|
...append,
|
||||||
|
])),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
widgets.add(ListTile(
|
||||||
|
onTap: () {
|
||||||
|
addPatAccount();
|
||||||
|
},
|
||||||
|
title: const Text("PAT入会"),
|
||||||
|
subtitle: const Text("点击绑定"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPatAccount() async {
|
||||||
|
print(jsonEncode(proInfoPat));
|
||||||
|
String? key = await inputString(context, "请输入授权代码");
|
||||||
|
if (key != null) {
|
||||||
|
await Navigator.of(context)
|
||||||
|
.push(mixRoute(builder: (BuildContext context) {
|
||||||
|
return AccessKeyReplaceScreen(accessKey: key);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPatAccount() async {
|
||||||
|
defaultToast(context, "请稍后");
|
||||||
|
try {
|
||||||
|
await method.reloadPatAccount();
|
||||||
|
await reloadIsPro();
|
||||||
|
defaultToast(context, "SUCCESS");
|
||||||
|
} catch (e) {
|
||||||
|
defaultToast(context, "FAIL : $e");
|
||||||
|
} finally {}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindThisAccount() async {
|
||||||
|
defaultToast(context, "请稍后");
|
||||||
|
try {
|
||||||
|
await method.bindThisAccount();
|
||||||
|
await reloadIsPro();
|
||||||
|
defaultToast(context, "SUCCESS");
|
||||||
|
} catch (e) {
|
||||||
|
defaultToast(context, "FAIL : $e");
|
||||||
|
} finally {}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearPat() async {
|
||||||
|
await method.clearPat();
|
||||||
|
await reloadIsPro();
|
||||||
|
defaultToast(context, "Success");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@ import 'package:pikapika/screens/components/RightClickPop.dart';
|
||||||
import '../basic/Entities.dart';
|
import '../basic/Entities.dart';
|
||||||
import '../basic/config/Address.dart';
|
import '../basic/config/Address.dart';
|
||||||
import '../basic/config/IconLoading.dart';
|
import '../basic/config/IconLoading.dart';
|
||||||
|
import 'components/ComicList.dart';
|
||||||
import 'components/ComicPager.dart';
|
import 'components/ComicPager.dart';
|
||||||
import 'components/Common.dart';
|
import 'components/Common.dart';
|
||||||
|
import 'components/GoDownloadSelect.dart';
|
||||||
|
|
||||||
// 搜索页面
|
// 搜索页面
|
||||||
class SearchScreen extends StatefulWidget {
|
class SearchScreen extends StatefulWidget {
|
||||||
|
@ -27,6 +29,7 @@ class SearchScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SearchScreenState extends State<SearchScreen> {
|
class _SearchScreenState extends State<SearchScreen> {
|
||||||
|
late final _comicListController = ComicListController();
|
||||||
late final TextEditingController _textEditController =
|
late final TextEditingController _textEditController =
|
||||||
TextEditingController(text: widget.keyword);
|
TextEditingController(text: widget.keyword);
|
||||||
late final SearchBar _searchBar = SearchBar(
|
late final SearchBar _searchBar = SearchBar(
|
||||||
|
@ -51,7 +54,11 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
title: Text("${categoryTitle(widget.category)} ${widget.keyword}"),
|
title: Text("${categoryTitle(widget.category)} ${widget.keyword}"),
|
||||||
actions: [
|
actions: [
|
||||||
commonPopMenu(context),
|
commonPopMenu(
|
||||||
|
context,
|
||||||
|
setState: setState,
|
||||||
|
comicListController: _comicListController,
|
||||||
|
),
|
||||||
addressPopMenu(context),
|
addressPopMenu(context),
|
||||||
_chooseCategoryAction(),
|
_chooseCategoryAction(),
|
||||||
_searchBar.getSearchAction(context),
|
_searchBar.getSearchAction(context),
|
||||||
|
@ -66,7 +73,7 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||||
categoryTitle(null),
|
categoryTitle(null),
|
||||||
...filteredList(
|
...filteredList(
|
||||||
storedCategories,
|
storedCategories,
|
||||||
(c) => !shadowCategories.contains(c),
|
(c) => !shadowCategories.contains(c),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
|
@ -100,7 +107,7 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context){
|
Widget build(BuildContext context) {
|
||||||
return rightClickPop(
|
return rightClickPop(
|
||||||
child: buildScreen(context),
|
child: buildScreen(context),
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -110,7 +117,9 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||||
|
|
||||||
Widget buildScreen(BuildContext context) {
|
Widget buildScreen(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: _searchBar.build(context),
|
appBar: _comicListController.selecting
|
||||||
|
? downAppBar(context, _comicListController, setState)
|
||||||
|
: _searchBar.build(context),
|
||||||
body: ComicPager(
|
body: ComicPager(
|
||||||
fetchPage: _fetch,
|
fetchPage: _fetch,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapika/basic/Common.dart';
|
||||||
import 'package:pikapika/screens/components/ComicList.dart';
|
import 'package:pikapika/screens/components/ComicList.dart';
|
||||||
|
|
||||||
import '../../basic/config/IsPro.dart';
|
import '../../basic/config/IsPro.dart';
|
||||||
|
@ -39,9 +40,12 @@ Widget commonPopMenu(
|
||||||
PopupMenuItem<int>(
|
PopupMenuItem<int>(
|
||||||
value: 3,
|
value: 3,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.download),
|
leading: Icon(
|
||||||
|
Icons.download,
|
||||||
|
color: isPro ? null : Colors.grey,
|
||||||
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
"下载" + (isPro ? "" : "Pro"),
|
"批量下载" + (isPro ? "" : "(发电)"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isPro ? null : Colors.grey,
|
color: isPro ? null : Colors.grey,
|
||||||
),
|
),
|
||||||
|
@ -63,6 +67,10 @@ Widget commonPopMenu(
|
||||||
chooseShadowCategories(context);
|
chooseShadowCategories(context);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
if (!isPro) {
|
||||||
|
defaultToast(context, "请先发电呀");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (setState != null) {
|
if (setState != null) {
|
||||||
if (comicListController != null) {
|
if (comicListController != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
|
@ -58,13 +58,15 @@ AppBar downAppBar(
|
||||||
MaterialButton(
|
MaterialButton(
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// todo
|
var list = _comicListController.selected;
|
||||||
final list = _comicListController.selected;
|
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
defaultToast(context, "请选择漫画");
|
defaultToast(context, "请选择漫画");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_comicListController.selecting = false;
|
list = list.toList();
|
||||||
|
setState((){
|
||||||
|
_comicListController.selecting = false;
|
||||||
|
});
|
||||||
Navigator.of(context).push(MaterialPageRoute(
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return DownloadComicsScreen(list);
|
return DownloadComicsScreen(list);
|
||||||
|
|
18
pubspec.lock
18
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
|
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.6"
|
version: "3.3.7"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -273,10 +273,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_ios
|
name: image_picker_ios
|
||||||
sha256: d4cb8ab04f770dab9d04c7959e5f6d22e8c5280343d425f9344f93832cf58445
|
sha256: a1546ff5861fc15812953d4733b520c3d371cec3d2859a001ff04c46c4d81883
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.7+2"
|
version: "0.8.7+3"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -286,7 +286,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.3"
|
version: "2.6.3"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
||||||
|
@ -574,10 +574,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92"
|
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.3"
|
version: "6.1.4"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -630,10 +630,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.4"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -51,6 +51,7 @@ dependencies:
|
||||||
uri_to_file: ^0.2.0
|
uri_to_file: ^0.2.0
|
||||||
uni_links: ^0.5.1
|
uni_links: ^0.5.1
|
||||||
filesystem_picker: ^3.0.0-beta.1
|
filesystem_picker: ^3.0.0-beta.1
|
||||||
|
intl: ^0.17.0
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
Loading…
Reference in New Issue