mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
437 lines
16 KiB
Dart
437 lines
16 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:PiliPlus/build_config.dart';
|
|
import 'package:PiliPlus/common/widgets/dialog.dart';
|
|
import 'package:PiliPlus/services/loggeer.dart';
|
|
import 'package:PiliPlus/utils/accounts/account.dart';
|
|
import 'package:PiliPlus/utils/login_utils.dart';
|
|
import 'package:PiliPlus/utils/page_utils.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:PiliPlus/utils/storage.dart';
|
|
import 'package:PiliPlus/utils/utils.dart';
|
|
import '../../utils/cache_manage.dart';
|
|
import '../mine/controller.dart';
|
|
|
|
class AboutPage extends StatefulWidget {
|
|
const AboutPage({super.key, this.showAppBar});
|
|
|
|
final bool? showAppBar;
|
|
|
|
@override
|
|
State<AboutPage> createState() => _AboutPageState();
|
|
}
|
|
|
|
class _AboutPageState extends State<AboutPage> {
|
|
final String _sourceCodeUrl = 'https://github.com/bggRGjQaUbCoE/PiliPlus';
|
|
|
|
RxString currentVersion = ''.obs;
|
|
RxString cacheSize = ''.obs;
|
|
|
|
late int _pressCount = 0;
|
|
|
|
late Color outline;
|
|
late TextStyle subTitleStyle;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
getCacheSize();
|
|
getCurrentApp();
|
|
}
|
|
|
|
Future getCacheSize() async {
|
|
cacheSize.value = await CacheManage().loadApplicationCache();
|
|
}
|
|
|
|
Future getCurrentApp() async {
|
|
var currentInfo = await PackageInfo.fromPlatform();
|
|
String buildNumber = currentInfo.buildNumber;
|
|
currentVersion.value = "${currentInfo.version}+$buildNumber";
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
outline = Theme.of(context).colorScheme.outline;
|
|
subTitleStyle =
|
|
TextStyle(fontSize: 13, color: Theme.of(context).colorScheme.outline);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar:
|
|
widget.showAppBar == false ? null : AppBar(title: const Text('关于')),
|
|
body: ListView(
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
_pressCount++;
|
|
if (_pressCount == 5) {
|
|
_pressCount = 0;
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return AlertDialog(
|
|
content: TextField(
|
|
autofocus: true,
|
|
onSubmitted: (value) {
|
|
Get.back();
|
|
if (value.isNotEmpty) {
|
|
PageUtils.handleWebview(value, inApp: true);
|
|
}
|
|
},
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
},
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxHeight: 150),
|
|
child: ExcludeSemantics(
|
|
child: Image.asset(
|
|
'assets/images/logo/logo.png',
|
|
),
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
title: Text(
|
|
'PiliPlus',
|
|
textAlign: TextAlign.center,
|
|
style:
|
|
Theme.of(context).textTheme.titleMedium!.copyWith(height: 2),
|
|
),
|
|
subtitle: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
'使用Flutter开发的B站第三方客户端',
|
|
style: TextStyle(color: outline),
|
|
semanticsLabel: '与你一起,发现不一样的世界',
|
|
),
|
|
const Icon(
|
|
Icons.accessibility_new,
|
|
semanticLabel: "无障碍适配",
|
|
size: 18,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Obx(
|
|
() => ListTile(
|
|
onTap: () => Utils.checkUpdate(false),
|
|
onLongPress: () => Utils.copyText(currentVersion.value),
|
|
title: const Text('当前版本'),
|
|
leading: const Icon(Icons.commit_outlined),
|
|
trailing: Text(
|
|
currentVersion.value,
|
|
style: subTitleStyle,
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
title: Text(
|
|
'''
|
|
Build Time: ${BuildConfig.buildTime}
|
|
Commit Hash: ${BuildConfig.commitHash}''',
|
|
style: TextStyle(fontSize: 14),
|
|
),
|
|
leading: const Icon(Icons.info_outline),
|
|
onTap: () => PageUtils.launchURL(
|
|
'https://github.com/bggRGjQaUbCoE/PiliPlus/commit/${BuildConfig.commitHash}'),
|
|
onLongPress: () => Utils.copyText(BuildConfig.commitHash),
|
|
),
|
|
Divider(
|
|
thickness: 1,
|
|
height: 30,
|
|
color: Theme.of(context).colorScheme.outlineVariant,
|
|
),
|
|
ListTile(
|
|
onTap: () => PageUtils.launchURL(_sourceCodeUrl),
|
|
leading: const Icon(Icons.code),
|
|
title: const Text('Source Code'),
|
|
subtitle: Text(_sourceCodeUrl, style: subTitleStyle),
|
|
),
|
|
if (Platform.isAndroid)
|
|
ListTile(
|
|
onTap: () {
|
|
Utils.channel.invokeMethod('linkVerifySettings');
|
|
},
|
|
leading: Icon(MdiIcons.linkBoxOutline),
|
|
title: const Text('打开受支持的链接'),
|
|
trailing: Icon(
|
|
Icons.arrow_forward,
|
|
size: 16,
|
|
color: outline,
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () {
|
|
PageUtils.launchURL('$_sourceCodeUrl/issues');
|
|
},
|
|
leading: const Icon(Icons.feedback_outlined),
|
|
title: const Text('问题反馈'),
|
|
trailing: Icon(
|
|
Icons.arrow_forward,
|
|
size: 16,
|
|
color: outline,
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () {
|
|
Get.toNamed('/logs');
|
|
},
|
|
onLongPress: clearLogs,
|
|
leading: const Icon(Icons.bug_report_outlined),
|
|
title: const Text('错误日志'),
|
|
subtitle: Text('长按清除日志', style: subTitleStyle),
|
|
trailing: Icon(Icons.arrow_forward, size: 16, color: outline),
|
|
),
|
|
ListTile(
|
|
onTap: () {
|
|
showConfirmDialog(
|
|
context: context,
|
|
title: '提示',
|
|
content: '该操作将清除图片及网络请求缓存数据,确认清除?',
|
|
onConfirm: () async {
|
|
SmartDialog.showLoading(msg: '正在清除...');
|
|
try {
|
|
await CacheManage.clearLibraryCache();
|
|
SmartDialog.showToast('清除成功');
|
|
} catch (err) {
|
|
SmartDialog.showToast(err.toString());
|
|
} finally {
|
|
SmartDialog.dismiss();
|
|
}
|
|
getCacheSize();
|
|
},
|
|
);
|
|
},
|
|
leading: const Icon(Icons.delete_outline),
|
|
title: const Text('清除缓存'),
|
|
subtitle: Obx(
|
|
() => Text(
|
|
'图片及网络缓存 ${cacheSize.value}',
|
|
style: subTitleStyle,
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
title: const Text('导入/导出登录信息'),
|
|
leading: const Icon(Icons.import_export_outlined),
|
|
onTap: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => SimpleDialog(
|
|
title: const Text('导入/导出登录信息'),
|
|
clipBehavior: Clip.hardEdge,
|
|
children: [
|
|
ListTile(
|
|
title: const Text('导出'),
|
|
onTap: () async {
|
|
Get.back();
|
|
String res = jsonEncode(Accounts.account.toMap());
|
|
Utils.copyText(res);
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text('导入'),
|
|
onTap: () async {
|
|
Get.back();
|
|
ClipboardData? data =
|
|
await Clipboard.getData('text/plain');
|
|
if (data?.text?.isNotEmpty != true) {
|
|
SmartDialog.showToast('剪贴板无数据');
|
|
return;
|
|
}
|
|
if (!context.mounted) return;
|
|
await showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return AlertDialog(
|
|
title: const Text('是否导入以下登录信息?'),
|
|
content: SingleChildScrollView(
|
|
child: Text(data!.text!),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: Get.back,
|
|
child: Text(
|
|
'取消',
|
|
style: TextStyle(color: outline),
|
|
),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
Get.back();
|
|
try {
|
|
final res = (jsonDecode(data.text!)
|
|
as Map)
|
|
.map((key, value) => MapEntry(key,
|
|
LoginAccount.fromJson(value)));
|
|
Accounts.account
|
|
.putAll(res)
|
|
.then((_) => Accounts.refresh())
|
|
.then((_) {
|
|
MineController.anonymity.value =
|
|
!Accounts.get(AccountType.heartbeat)
|
|
.isLogin;
|
|
if (Accounts.main.isLogin) {
|
|
return LoginUtils.onLoginMain();
|
|
}
|
|
});
|
|
} catch (e) {
|
|
SmartDialog.showToast('导入失败:$e');
|
|
}
|
|
},
|
|
child: const Text('确定'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text('导入/导出设置'),
|
|
dense: false,
|
|
leading: const Icon(Icons.import_export_outlined),
|
|
onTap: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return SimpleDialog(
|
|
clipBehavior: Clip.hardEdge,
|
|
title: const Text('导入/导出设置'),
|
|
children: [
|
|
ListTile(
|
|
title: const Text('导出设置至剪贴板'),
|
|
onTap: () async {
|
|
Get.back();
|
|
String data = await GStorage.exportAllSettings();
|
|
Utils.copyText(data);
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text('从剪贴板导入设置'),
|
|
onTap: () async {
|
|
Get.back();
|
|
ClipboardData? data =
|
|
await Clipboard.getData('text/plain');
|
|
if (data == null ||
|
|
data.text == null ||
|
|
data.text!.isEmpty) {
|
|
SmartDialog.showToast('剪贴板无数据');
|
|
return;
|
|
}
|
|
if (!context.mounted) return;
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return AlertDialog(
|
|
title: const Text('是否导入如下设置?'),
|
|
content: SingleChildScrollView(
|
|
child: Text(data.text!),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: Get.back,
|
|
child: Text(
|
|
'取消',
|
|
style: TextStyle(color: outline),
|
|
),
|
|
),
|
|
TextButton(
|
|
onPressed: () async {
|
|
Get.back();
|
|
try {
|
|
await GStorage.importAllSettings(
|
|
data.text!);
|
|
SmartDialog.showToast('导入成功');
|
|
} catch (e) {
|
|
SmartDialog.showToast('导入失败:$e');
|
|
}
|
|
},
|
|
child: const Text('确定'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text('重置所有设置'),
|
|
leading: const Icon(Icons.settings_backup_restore_outlined),
|
|
onTap: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return AlertDialog(
|
|
title: const Text('重置所有设置'),
|
|
content: const Text('是否重置所有设置?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Get.back();
|
|
},
|
|
child: const Text('取消'),
|
|
),
|
|
TextButton(
|
|
onPressed: () async {
|
|
Get.back();
|
|
await Future.wait([
|
|
GStorage.setting.clear(),
|
|
GStorage.video.clear(),
|
|
]);
|
|
SmartDialog.showToast('重置成功');
|
|
},
|
|
child: const Text('重置可导出的设置'),
|
|
),
|
|
TextButton(
|
|
onPressed: () async {
|
|
Get.back();
|
|
await Future.wait([
|
|
GStorage.userInfo.clear(),
|
|
GStorage.setting.clear(),
|
|
GStorage.localCache.clear(),
|
|
GStorage.video.clear(),
|
|
GStorage.historyWord.clear(),
|
|
Accounts.clear(),
|
|
]);
|
|
SmartDialog.showToast('重置成功');
|
|
},
|
|
child: const Text('重置所有数据(含登录信息)'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 80),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|