mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 08:38:18 +08:00
Compare commits
3 Commits
db7ad269b2
...
bac0769933
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bac0769933 | ||
|
|
24f2cfa4e9 | ||
|
|
970ee679f1 |
@@ -16,10 +16,11 @@ import 'package:PiliPlus/utils/storage_pref.dart';
|
|||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:archive/archive.dart';
|
import 'package:archive/archive.dart';
|
||||||
import 'package:brotli/brotli.dart';
|
import 'package:brotli/brotli.dart';
|
||||||
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
import 'package:flutter/foundation.dart' show kDebugMode, listEquals;
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
static const _gzipDecoder = GZipDecoder();
|
static const _gzipDecoder = GZipDecoder();
|
||||||
@@ -115,28 +116,24 @@ class Request {
|
|||||||
return h11;
|
return h11;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static Timer? _networkChangeDebounce;
|
||||||
* config it and create
|
|
||||||
*/
|
|
||||||
Request._internal() {
|
|
||||||
//BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
|
|
||||||
BaseOptions options = BaseOptions(
|
|
||||||
//请求基地址,可以包含子路径
|
|
||||||
baseUrl: HttpString.apiBaseUrl,
|
|
||||||
//连接服务器超时时间,单位是毫秒.
|
|
||||||
connectTimeout: const Duration(milliseconds: 10000),
|
|
||||||
//响应流上前后两次接受到数据的间隔,单位为毫秒。
|
|
||||||
receiveTimeout: const Duration(milliseconds: 10000),
|
|
||||||
//Http请求头.
|
|
||||||
headers: {
|
|
||||||
'user-agent': 'Dart/3.6 (dart:io)', // Http2Adapter不会自动添加标头
|
|
||||||
if (!_enableHttp2) 'connection': 'keep-alive',
|
|
||||||
'accept-encoding': 'br,gzip',
|
|
||||||
},
|
|
||||||
responseDecoder: _responseDecoder, // Http2Adapter没有自动解压
|
|
||||||
persistentConnection: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
static void _onConnectivityChanged(List<ConnectivityResult> result) {
|
||||||
|
if (listEquals(result, const [ConnectivityResult.none])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_networkChangeDebounce?.cancel();
|
||||||
|
_networkChangeDebounce = Timer(
|
||||||
|
const Duration(milliseconds: 500),
|
||||||
|
_resetAdaptersForNetworkChange,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _watchConnectivity() {
|
||||||
|
Connectivity().onConnectivityChanged.skip(1).listen(_onConnectivityChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
static (IOHttpClientAdapter, ConnectionManager?) _createPool() {
|
||||||
final bool enableSystemProxy;
|
final bool enableSystemProxy;
|
||||||
late final String systemProxyHost;
|
late final String systemProxyHost;
|
||||||
late final int? systemProxyPort;
|
late final int? systemProxyPort;
|
||||||
@@ -160,30 +157,72 @@ class Request {
|
|||||||
..autoUncompress = false, // Http2Adapter没有自动解压, 统一行为
|
..autoUncompress = false, // Http2Adapter没有自动解压, 统一行为
|
||||||
);
|
);
|
||||||
|
|
||||||
dio = Dio(options)
|
final connectionManager = _enableHttp2
|
||||||
..httpClientAdapter = _enableHttp2
|
? ConnectionManager(
|
||||||
? Http2Adapter(
|
|
||||||
ConnectionManager(
|
|
||||||
idleTimeout: const Duration(seconds: 15),
|
idleTimeout: const Duration(seconds: 15),
|
||||||
onClientCreate: enableSystemProxy
|
onClientCreate: enableSystemProxy
|
||||||
? (_, config) {
|
? (_, config) => config
|
||||||
config
|
|
||||||
..proxy = Uri(
|
..proxy = Uri(
|
||||||
scheme: 'http',
|
scheme: 'http',
|
||||||
host: systemProxyHost,
|
host: systemProxyHost,
|
||||||
port: systemProxyPort,
|
port: systemProxyPort,
|
||||||
)
|
)
|
||||||
..onBadCertificate = (_) => true;
|
..onBadCertificate = (_) => true
|
||||||
}
|
|
||||||
: Pref.badCertificateCallback
|
: Pref.badCertificateCallback
|
||||||
? (_, config) {
|
? (_, config) => config.onBadCertificate = (_) => true
|
||||||
config.onBadCertificate = (_) => true;
|
|
||||||
}
|
|
||||||
: null,
|
: null,
|
||||||
),
|
|
||||||
fallbackAdapter: http11Adapter,
|
|
||||||
)
|
)
|
||||||
: http11Adapter;
|
: null;
|
||||||
|
return (http11Adapter, connectionManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:notify-debugger-on-exception')
|
||||||
|
static void _resetAdaptersForNetworkChange() {
|
||||||
|
try {
|
||||||
|
final (h11, connectionManager) = _createPool();
|
||||||
|
if (connectionManager != null) {
|
||||||
|
(dio.httpClientAdapter as Http2Adapter)
|
||||||
|
..connectionManager.close(force: true)
|
||||||
|
..connectionManager = connectionManager
|
||||||
|
..fallbackAdapter.close(force: true)
|
||||||
|
..fallbackAdapter = h11;
|
||||||
|
_http11Dio?.httpClientAdapter = h11;
|
||||||
|
} else {
|
||||||
|
dio
|
||||||
|
..httpClientAdapter.close(force: true)
|
||||||
|
..httpClientAdapter = h11;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* config it and create
|
||||||
|
*/
|
||||||
|
Request._internal() {
|
||||||
|
//BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
|
||||||
|
BaseOptions options = BaseOptions(
|
||||||
|
//请求基地址,可以包含子路径
|
||||||
|
baseUrl: HttpString.apiBaseUrl,
|
||||||
|
//连接服务器超时时间,单位是毫秒.
|
||||||
|
connectTimeout: const Duration(milliseconds: 10000),
|
||||||
|
//响应流上前后两次接受到数据的间隔,单位为毫秒。
|
||||||
|
receiveTimeout: const Duration(milliseconds: 10000),
|
||||||
|
//Http请求头.
|
||||||
|
headers: {
|
||||||
|
'user-agent': 'Dart/3.6 (dart:io)', // Http2Adapter不会自动添加标头
|
||||||
|
if (!_enableHttp2) 'connection': 'keep-alive',
|
||||||
|
'accept-encoding': 'br,gzip',
|
||||||
|
},
|
||||||
|
responseDecoder: _responseDecoder, // Http2Adapter没有自动解压
|
||||||
|
persistentConnection: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
final (h11, connectionManager) = _createPool();
|
||||||
|
|
||||||
|
dio = Dio(options)
|
||||||
|
..httpClientAdapter = _enableHttp2
|
||||||
|
? Http2Adapter(connectionManager, fallbackAdapter: h11)
|
||||||
|
: h11;
|
||||||
|
|
||||||
// 先于其他Interceptor
|
// 先于其他Interceptor
|
||||||
if (Pref.retryCount != 0) {
|
if (Pref.retryCount != 0) {
|
||||||
@@ -208,6 +247,8 @@ class Request {
|
|||||||
..options.validateStatus = (int? status) {
|
..options.validateStatus = (int? status) {
|
||||||
return status != null && status >= 200 && status < 300;
|
return status != null && status >= 200 && status < 300;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Platform.isIOS) _watchConnectivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -70,13 +70,11 @@ List<SettingsModel> get extraSettings => [
|
|||||||
onTap: _showDownPathDialog,
|
onTap: _showDownPathDialog,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
SwitchModel(
|
SplitModel(
|
||||||
|
normalModel: const NormalModel.split(
|
||||||
title: '空降助手',
|
title: '空降助手',
|
||||||
subtitle: '点击配置',
|
subtitle: '点击配置',
|
||||||
setKey: SettingBoxKey.enableSponsorBlock,
|
leading: Stack(
|
||||||
defaultVal: false,
|
|
||||||
onTap: (context) => Get.toNamed('/sponsorBlock'),
|
|
||||||
leading: const Stack(
|
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -85,6 +83,12 @@ List<SettingsModel> get extraSettings => [
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
switchModel: SwitchModel.split(
|
||||||
|
defaultVal: false,
|
||||||
|
setKey: SettingBoxKey.enableSponsorBlock,
|
||||||
|
onTap: (context) => Get.toNamed('/sponsorBlock'),
|
||||||
|
),
|
||||||
|
),
|
||||||
PopupModel<SkipType>(
|
PopupModel<SkipType>(
|
||||||
title: '番剧片头/片尾跳过类型',
|
title: '番剧片头/片尾跳过类型',
|
||||||
leading: const Icon(MdiIcons.debugStepOver),
|
leading: const Icon(MdiIcons.debugStepOver),
|
||||||
@@ -94,15 +98,19 @@ List<SettingsModel> get extraSettings => [
|
|||||||
.put(SettingBoxKey.pgcSkipType, value.index)
|
.put(SettingBoxKey.pgcSkipType, value.index)
|
||||||
.whenComplete(setState),
|
.whenComplete(setState),
|
||||||
),
|
),
|
||||||
SwitchModel(
|
SplitModel(
|
||||||
|
normalModel: const NormalModel.split(
|
||||||
title: '检查未读动态',
|
title: '检查未读动态',
|
||||||
subtitle: '点击设置检查周期(min)',
|
subtitle: '点击设置检查周期(min)',
|
||||||
leading: const Icon(Icons.notifications_none),
|
leading: Icon(Icons.notifications_none),
|
||||||
setKey: SettingBoxKey.checkDynamic,
|
),
|
||||||
|
switchModel: SwitchModel.split(
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
|
setKey: SettingBoxKey.checkDynamic,
|
||||||
onChanged: (value) => Get.find<MainController>().checkDynamic = value,
|
onChanged: (value) => Get.find<MainController>().checkDynamic = value,
|
||||||
onTap: _showDynDialog,
|
onTap: _showDynDialog,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
SwitchModel(
|
SwitchModel(
|
||||||
title: '显示视频分段信息',
|
title: '显示视频分段信息',
|
||||||
leading: Transform.rotate(
|
leading: Transform.rotate(
|
||||||
@@ -615,13 +623,18 @@ List<SettingsModel> get extraSettings => [
|
|||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
onChanged: (value) => MemberTabType.showMemberShop = value,
|
onChanged: (value) => MemberTabType.showMemberShop = value,
|
||||||
),
|
),
|
||||||
const SwitchModel(
|
const SplitModel(
|
||||||
leading: Icon(Icons.airplane_ticket_outlined),
|
normalModel: NormalModel.split(
|
||||||
title: '设置代理',
|
title: '设置代理',
|
||||||
subtitle: '设置代理 host:port',
|
subtitle: '设置代理 host:port',
|
||||||
|
leading: Icon(Icons.airplane_ticket_outlined),
|
||||||
|
),
|
||||||
|
switchModel: SwitchModel.split(
|
||||||
|
defaultVal: false,
|
||||||
setKey: SettingBoxKey.enableSystemProxy,
|
setKey: SettingBoxKey.enableSystemProxy,
|
||||||
onTap: _showProxyDialog,
|
onTap: _showProxyDialog,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SwitchModel(
|
const SwitchModel(
|
||||||
title: '自动清除缓存',
|
title: '自动清除缓存',
|
||||||
subtitle: '每次启动时清除缓存',
|
subtitle: '每次启动时清除缓存',
|
||||||
|
|||||||
@@ -30,6 +30,43 @@ sealed class SettingsModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SplitModel extends SettingsModel {
|
||||||
|
const SplitModel({
|
||||||
|
super.contentPadding,
|
||||||
|
super.titleStyle,
|
||||||
|
required this.normalModel,
|
||||||
|
required this.switchModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get effectiveSubtitle => normalModel.effectiveSubtitle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get effectiveTitle => normalModel.effectiveTitle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get title => normalModel.title;
|
||||||
|
|
||||||
|
final NormalModel normalModel;
|
||||||
|
|
||||||
|
final SwitchModel switchModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget get widget => SetSwitchItem(
|
||||||
|
title: effectiveTitle,
|
||||||
|
subtitle: effectiveSubtitle,
|
||||||
|
setKey: switchModel.setKey,
|
||||||
|
defaultVal: switchModel.defaultVal,
|
||||||
|
onChanged: switchModel.onChanged,
|
||||||
|
needReboot: switchModel.needReboot,
|
||||||
|
leading: normalModel.leading,
|
||||||
|
onTap: switchModel.onTap,
|
||||||
|
contentPadding: contentPadding,
|
||||||
|
titleStyle: titleStyle,
|
||||||
|
isSplit: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class PopupModel<T extends EnumWithLabel> extends SettingsModel {
|
class PopupModel<T extends EnumWithLabel> extends SettingsModel {
|
||||||
const PopupModel({
|
const PopupModel({
|
||||||
required this.title,
|
required this.title,
|
||||||
@@ -88,6 +125,18 @@ class NormalModel extends SettingsModel {
|
|||||||
this.onTap,
|
this.onTap,
|
||||||
}) : assert(title != null || getTitle != null);
|
}) : assert(title != null || getTitle != null);
|
||||||
|
|
||||||
|
const NormalModel.split({
|
||||||
|
super.subtitle,
|
||||||
|
super.leading,
|
||||||
|
super.contentPadding,
|
||||||
|
super.titleStyle,
|
||||||
|
this.title,
|
||||||
|
this.getTitle,
|
||||||
|
this.getSubtitle,
|
||||||
|
this.getTrailing,
|
||||||
|
}) : onTap = null,
|
||||||
|
assert(title != null || getTitle != null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get effectiveTitle => title ?? getTitle!();
|
String get effectiveTitle => title ?? getTitle!();
|
||||||
@override
|
@override
|
||||||
@@ -109,7 +158,7 @@ class NormalModel extends SettingsModel {
|
|||||||
|
|
||||||
class SwitchModel extends SettingsModel {
|
class SwitchModel extends SettingsModel {
|
||||||
@override
|
@override
|
||||||
final String title;
|
final String? title;
|
||||||
final String setKey;
|
final String setKey;
|
||||||
final bool defaultVal;
|
final bool defaultVal;
|
||||||
final ValueChanged<bool>? onChanged;
|
final ValueChanged<bool>? onChanged;
|
||||||
@@ -121,7 +170,7 @@ class SwitchModel extends SettingsModel {
|
|||||||
super.leading,
|
super.leading,
|
||||||
super.contentPadding,
|
super.contentPadding,
|
||||||
super.titleStyle,
|
super.titleStyle,
|
||||||
required this.title,
|
required String this.title,
|
||||||
required this.setKey,
|
required this.setKey,
|
||||||
this.defaultVal = false,
|
this.defaultVal = false,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
@@ -129,14 +178,22 @@ class SwitchModel extends SettingsModel {
|
|||||||
this.onTap,
|
this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const SwitchModel.split({
|
||||||
|
required this.setKey,
|
||||||
|
this.defaultVal = false,
|
||||||
|
this.needReboot = false,
|
||||||
|
this.onChanged,
|
||||||
|
this.onTap,
|
||||||
|
}) : title = null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get effectiveTitle => title;
|
String get effectiveTitle => title!;
|
||||||
@override
|
@override
|
||||||
String? get effectiveSubtitle => subtitle;
|
String? get effectiveSubtitle => subtitle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget get widget => SetSwitchItem(
|
Widget get widget => SetSwitchItem(
|
||||||
title: title,
|
title: title!,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
setKey: setKey,
|
setKey: setKey,
|
||||||
defaultVal: defaultVal,
|
defaultVal: defaultVal,
|
||||||
|
|||||||
@@ -82,15 +82,19 @@ List<SettingsModel> get styleSettings => [
|
|||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
needReboot: true,
|
needReboot: true,
|
||||||
),
|
),
|
||||||
SwitchModel(
|
SplitModel(
|
||||||
|
normalModel: const NormalModel.split(
|
||||||
title: 'App字体字重',
|
title: 'App字体字重',
|
||||||
subtitle: '点击设置',
|
subtitle: '点击设置',
|
||||||
setKey: SettingBoxKey.appFontWeight,
|
leading: Icon(Icons.text_fields),
|
||||||
|
),
|
||||||
|
switchModel: SwitchModel.split(
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
leading: const Icon(Icons.text_fields),
|
setKey: SettingBoxKey.appFontWeight,
|
||||||
onChanged: (_) => Get.updateMyAppTheme(),
|
onChanged: (_) => Get.updateMyAppTheme(),
|
||||||
onTap: _showFontWeightDialog,
|
onTap: _showFontWeightDialog,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
NormalModel(
|
NormalModel(
|
||||||
title: '界面缩放',
|
title: '界面缩放',
|
||||||
getSubtitle: () => '当前缩放比例:${Pref.uiScale.toStringAsFixed(2)}',
|
getSubtitle: () => '当前缩放比例:${Pref.uiScale.toStringAsFixed(2)}',
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
final void Function(BuildContext context)? onTap;
|
final void Function(BuildContext context)? onTap;
|
||||||
final EdgeInsetsGeometry? contentPadding;
|
final EdgeInsetsGeometry? contentPadding;
|
||||||
final TextStyle? titleStyle;
|
final TextStyle? titleStyle;
|
||||||
|
final bool isSplit;
|
||||||
|
|
||||||
const SetSwitchItem({
|
const SetSwitchItem({
|
||||||
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
required this.setKey,
|
required this.setKey,
|
||||||
@@ -29,7 +31,7 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
this.onTap,
|
this.onTap,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.titleStyle,
|
this.titleStyle,
|
||||||
super.key,
|
this.isSplit = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -103,7 +105,17 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
|||||||
final subTitleStyle = theme.textTheme.labelMedium!.copyWith(
|
final subTitleStyle = theme.textTheme.labelMedium!.copyWith(
|
||||||
color: theme.colorScheme.outline,
|
color: theme.colorScheme.outline,
|
||||||
);
|
);
|
||||||
return ListTile(
|
|
||||||
|
final switchBtn = Transform.scale(
|
||||||
|
scale: 0.8,
|
||||||
|
alignment: .centerRight,
|
||||||
|
child: Switch(
|
||||||
|
value: val,
|
||||||
|
onChanged: switchChange,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget child(Widget? trailing) => ListTile(
|
||||||
contentPadding: widget.contentPadding,
|
contentPadding: widget.contentPadding,
|
||||||
enabled: widget.onTap == null ? true : val,
|
enabled: widget.onTap == null ? true : val,
|
||||||
onTap: widget.onTap == null ? switchChange : () => widget.onTap!(context),
|
onTap: widget.onTap == null ? switchChange : () => widget.onTap!(context),
|
||||||
@@ -112,14 +124,28 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
|||||||
? Text(widget.subtitle!, style: subTitleStyle)
|
? Text(widget.subtitle!, style: subTitleStyle)
|
||||||
: null,
|
: null,
|
||||||
leading: widget.leading,
|
leading: widget.leading,
|
||||||
trailing: Transform.scale(
|
trailing: trailing,
|
||||||
scale: 0.8,
|
);
|
||||||
alignment: .centerRight,
|
|
||||||
child: Switch(
|
if (widget.isSplit) {
|
||||||
value: val,
|
return Row(
|
||||||
onChanged: switchChange,
|
children: [
|
||||||
|
Expanded(child: child(null)),
|
||||||
|
SizedBox(
|
||||||
|
height: 25,
|
||||||
|
child: VerticalDivider(
|
||||||
|
width: 1,
|
||||||
|
color: theme.colorScheme.outline.withValues(alpha: .3),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const .only(left: 4, right: 24),
|
||||||
|
child: switchBtn,
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return child(switchBtn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user