diff --git a/lib/common/widgets/custom_toast.dart b/lib/common/widgets/custom_toast.dart index 78b47e5b5..196e32c7b 100644 --- a/lib/common/widgets/custom_toast.dart +++ b/lib/common/widgets/custom_toast.dart @@ -1,4 +1,4 @@ -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; class CustomToast extends StatelessWidget { @@ -6,11 +6,11 @@ class CustomToast extends StatelessWidget { final String msg; + static double toastOpacity = Pref.defaultToastOp; + @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - final double toastOpacity = GStorage.setting - .get(SettingBoxKey.defaultToastOp, defaultValue: 1.0) as double; return Container( margin: EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom + 30), diff --git a/lib/common/widgets/image/image_view.dart b/lib/common/widgets/image/image_view.dart index 4d7dc9e33..d7c0b2aea 100644 --- a/lib/common/widgets/image/image_view.dart +++ b/lib/common/widgets/image/image_view.dart @@ -7,7 +7,7 @@ import 'package:PiliPlus/common/widgets/image/nine_grid_view.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; class ImageModel { @@ -28,7 +28,10 @@ class ImageModel { dynamic get safeWidth => width ?? 1; dynamic get safeHeight => height ?? 1; bool get isLongPic => _isLongPic ??= (safeHeight / safeWidth) > (22 / 9); - bool get isLivePhoto => _isLivePhoto ??= liveUrl?.isNotEmpty == true; + bool get isLivePhoto => + _isLivePhoto ??= enableLivePhoto && liveUrl?.isNotEmpty == true; + + static bool enableLivePhoto = Pref.enableLivePhoto; } Widget imageView( @@ -80,8 +83,6 @@ Widget imageView( ); } - late final enableLivePhoto = GStorage.enableLivePhoto; - int parseSize(size) { return switch (size) { int() => size, @@ -100,7 +101,7 @@ Widget imageView( initialPage: index, imgList: picArr.map( (item) { - bool isLive = item.isLivePhoto && enableLivePhoto; + bool isLive = item.isLivePhoto; return SourceModel( sourceType: isLive ? SourceType.livePhoto : SourceType.networkImage, diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index ec39ac8ff..592a28c39 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -6,7 +6,7 @@ import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactive_vi import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/image_util.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:device_info_plus/device_info_plus.dart'; @@ -95,7 +95,7 @@ class _InteractiveviewerGalleryState extends State late final RxInt currentIndex = widget.initIndex.obs; - late final int _quality = GStorage.previewQ; + late final int _quality = Pref.previewQ; @override void initState() { diff --git a/lib/common/widgets/pendant_avatar.dart b/lib/common/widgets/pendant_avatar.dart index c6623711e..ca7e5b4d1 100644 --- a/lib/common/widgets/pendant_avatar.dart +++ b/lib/common/widgets/pendant_avatar.dart @@ -3,7 +3,7 @@ import 'package:PiliPlus/models/common/avatar_badge_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/image_util.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -38,7 +38,7 @@ class PendantAvatar extends StatelessWidget { : BadgeType.none, badgeSize = badgeSize ?? size / 3; - static bool showDynDecorate = GStorage.showDynDecorate; + static bool showDynDecorate = Pref.showDynDecorate; @override Widget build(BuildContext context) { diff --git a/lib/common/widgets/refresh_indicator.dart b/lib/common/widgets/refresh_indicator.dart index d01151a16..4b18e9944 100644 --- a/lib/common/widgets/refresh_indicator.dart +++ b/lib/common/widgets/refresh_indicator.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:math' as math; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart' show clampDouble; import 'package:flutter/material.dart' hide RefreshIndicator; @@ -23,8 +24,8 @@ Widget refreshIndicator({ // The over-scroll distance that moves the indicator to its maximum // displacement, as a percentage of the scrollable's container extent. -double displacement = 20; -double kDragContainerExtentPercentage = 0.25; +double displacement = Pref.refreshDisplacement; +double kDragContainerExtentPercentage = Pref.refreshDragPercentage; // How much the scroll's drag gesture can overshoot the RefreshIndicator's // displacement; max displacement = _kDragSizeFactorLimit * displacement. diff --git a/lib/common/widgets/scroll_physics.dart b/lib/common/widgets/scroll_physics.dart index 9e0ef3b1a..88773c6b7 100644 --- a/lib/common/widgets/scroll_physics.dart +++ b/lib/common/widgets/scroll_physics.dart @@ -1,4 +1,4 @@ -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; Widget videoTabBarView({ @@ -73,14 +73,16 @@ class MemberVideoScrollPhysics extends AlwaysScrollableScrollPhysics { } class CustomSpringDescription implements SpringDescription { - @override - final mass = GStorage.springDescription[0]; + static final List springDescription = Pref.springDescription; @override - final stiffness = GStorage.springDescription[1]; + final mass = springDescription[0]; @override - final damping = GStorage.springDescription[2]; + final stiffness = springDescription[1]; + + @override + final damping = springDescription[2]; CustomSpringDescription._(); diff --git a/lib/common/widgets/video_popup_menu.dart b/lib/common/widgets/video_popup_menu.dart index bd7c66962..31697ea3f 100644 --- a/lib/common/widgets/video_popup_menu.dart +++ b/lib/common/widgets/video_popup_menu.dart @@ -6,7 +6,8 @@ import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/models_new/space/space_archive/item.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -249,7 +250,7 @@ class VideoCustomActions { act: 5, reSrc: 11, ); - GStorage.setBlackMid(videoItem.owner.mid!); + Pref.setBlackMid(videoItem.owner.mid!); Get.back(); SmartDialog.showToast(res['msg'] ?? '成功'); }, diff --git a/lib/grpc/grpc_repo.dart b/lib/grpc/grpc_repo.dart index 71b330a9e..1ec2f510c 100644 --- a/lib/grpc/grpc_repo.dart +++ b/lib/grpc/grpc_repo.dart @@ -12,9 +12,9 @@ import 'package:PiliPlus/grpc/google/rpc/status.pb.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/login_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:archive/archive.dart'; import 'package:dio/dio.dart'; diff --git a/lib/grpc/reply.dart b/lib/grpc/reply.dart index bfa1df7b8..3186ce937 100644 --- a/lib/grpc/reply.dart +++ b/lib/grpc/reply.dart @@ -3,10 +3,15 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'; import 'package:PiliPlus/grpc/bilibili/pagination.pb.dart'; import 'package:PiliPlus/grpc/grpc_repo.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/http/reply.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:fixnum/fixnum.dart'; class ReplyGrpc { + static bool antiGoodsReply = Pref.antiGoodsReply; + static RegExp replyRegExp = + RegExp(Pref.banWordForReply, caseSensitive: false); + static bool enableFilter = replyRegExp.pattern.isNotEmpty; + // static Future replyInfo({required int rpid}) { // return _request( // GrpcUrl.replyInfo, @@ -28,9 +33,8 @@ class ReplyGrpc { reply.content.message.contains(Constants.goodsUrlPrefix); } - static bool needRemoveGrpc(ReplyInfo reply, final bool antiGoodsReply) { - return (ReplyHttp.replyRegExp.pattern.isNotEmpty && - ReplyHttp.replyRegExp.hasMatch(reply.content.message)) || + static bool needRemoveGrpc(ReplyInfo reply) { + return (enableFilter && replyRegExp.hasMatch(reply.content.message)) || (antiGoodsReply && needRemoveGoodGrpc(reply)); } @@ -40,7 +44,6 @@ class ReplyGrpc { required Mode mode, required String? offset, required Int64? cursorNext, - required final bool antiGoodsReply, }) async { final res = await GrpcRepo.request( GrpcUrl.mainList, @@ -59,16 +62,15 @@ class ReplyGrpc { if (res.isSuccess) { final mainListReply = res.data; // keyword filter - if (mainListReply.hasUpTop() && - needRemoveGrpc(mainListReply.upTop, antiGoodsReply)) { + if (mainListReply.hasUpTop() && needRemoveGrpc(mainListReply.upTop)) { mainListReply.clearUpTop(); } if (mainListReply.replies.isNotEmpty) { mainListReply.replies.removeWhere((item) { - final hasMatch = needRemoveGrpc(item, antiGoodsReply); + final hasMatch = needRemoveGrpc(item); if (!hasMatch && item.replies.isNotEmpty) { - item.replies.removeWhere((i) => needRemoveGrpc(i, antiGoodsReply)); + item.replies.removeWhere((i) => needRemoveGrpc(i)); } return hasMatch; }); @@ -84,7 +86,6 @@ class ReplyGrpc { required int rpid, required Mode mode, required String? offset, - required final bool antiGoodsReply, }) async { final res = await GrpcRepo.request( GrpcUrl.detailList, @@ -100,10 +101,7 @@ class ReplyGrpc { DetailListReply.fromBuffer, ); return res - ..dataOrNull - ?.root - .replies - .removeWhere((item) => needRemoveGrpc(item, antiGoodsReply)); + ..dataOrNull?.root.replies.removeWhere((item) => needRemoveGrpc(item)); } static Future> dialogList({ @@ -112,7 +110,6 @@ class ReplyGrpc { required int root, required int dialog, required String? offset, - required final bool antiGoodsReply, }) async { final res = await GrpcRepo.request( GrpcUrl.dialogList, @@ -125,9 +122,6 @@ class ReplyGrpc { ), DialogListReply.fromBuffer, ); - return res - ..dataOrNull - ?.replies - .removeWhere((item) => needRemoveGrpc(item, antiGoodsReply)); + return res..dataOrNull?.replies.removeWhere((item) => needRemoveGrpc(item)); } } diff --git a/lib/http/black.dart b/lib/http/black.dart index 9eedd512f..4f4e4f4d5 100644 --- a/lib/http/black.dart +++ b/lib/http/black.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models_new/blacklist/data.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; class BlackHttp { static Future> blackList( diff --git a/lib/http/danmaku.dart b/lib/http/danmaku.dart index e94b7b54c..2681e9661 100644 --- a/lib/http/danmaku.dart +++ b/lib/http/danmaku.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:dio/dio.dart'; class DanmakuHttp { diff --git a/lib/http/danmaku_block.dart b/lib/http/danmaku_block.dart index 10a56c286..fbce701a1 100644 --- a/lib/http/danmaku_block.dart +++ b/lib/http/danmaku_block.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/user/danmaku_block.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; class DanmakuFilterHttp { static Future danmakuFilter() async { diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index b5eae64b3..ff6997525 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -15,8 +15,8 @@ import 'package:PiliPlus/models_new/dynamic/dyn_reserve/data.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_topic_feed/topic_card_list.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/top_details.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/topic_item.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; diff --git a/lib/http/fav.dart b/lib/http/fav.dart index 318ba81e4..345f4ec36 100644 --- a/lib/http/fav.dart +++ b/lib/http/fav.dart @@ -11,8 +11,8 @@ import 'package:PiliPlus/models_new/fav/fav_pgc/data.dart'; import 'package:PiliPlus/models_new/fav/fav_topic/data.dart'; import 'package:PiliPlus/models_new/space/space_fav/data.dart'; import 'package:PiliPlus/models_new/sub/sub_detail/data.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/app_sign.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; import 'package:dio/dio.dart'; class FavHttp { diff --git a/lib/http/init.dart b/lib/http/init.dart index e2a29de1b..d53c69310 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -5,10 +5,12 @@ import 'dart:io'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/retry_interceptor.dart'; import 'package:PiliPlus/http/user.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/accounts/account_manager/account_mgr.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:archive/archive.dart'; import 'package:brotli/brotli.dart'; import 'package:dio/dio.dart'; @@ -118,10 +120,9 @@ class Request { responseDecoder: responseDecoder, // Http2Adapter没有自动解压 persistentConnection: true); - final bool enableSystemProxy = GStorage.setting - .get(SettingBoxKey.enableSystemProxy, defaultValue: false); - final String systemProxyHost = GStorage.defaultSystemProxyHost; - final String systemProxyPort = GStorage.defaultSystemProxyPort; + final bool enableSystemProxy = Pref.enableSystemProxy; + final String systemProxyHost = Pref.systemProxyHost; + final String systemProxyPort = Pref.systemProxyPort; final http11Adapter = IOHttpClientAdapter(createHttpClient: () { final client = HttpClient() @@ -146,28 +147,26 @@ class Request { } dio = Dio(options) - ..httpClientAdapter = - GStorage.setting.get(SettingBoxKey.enableHttp2, defaultValue: false) - ? Http2Adapter( - ConnectionManager( - idleTimeout: const Duration(seconds: 15), - onClientCreate: enableSystemProxy + ..httpClientAdapter = Pref.enableHttp2 + ? Http2Adapter( + ConnectionManager( + idleTimeout: const Duration(seconds: 15), + onClientCreate: enableSystemProxy + ? (_, config) { + config + ..proxy = proxy + ..onBadCertificate = (_) => true; + } + : Pref.badCertificateCallback ? (_, config) { - config - ..proxy = proxy - ..onBadCertificate = (_) => true; + config.onBadCertificate = (_) => true; } - : GStorage.badCertificateCallback - ? (_, config) { - config.onBadCertificate = (_) => true; - } - : null), - fallbackAdapter: http11Adapter) - : http11Adapter; + : null), + fallbackAdapter: http11Adapter) + : http11Adapter; // 先于其他Interceptor - dio.interceptors - .add(RetryInterceptor(GStorage.retryCount, GStorage.retryDelay)); + dio.interceptors.add(RetryInterceptor(Pref.retryCount, Pref.retryDelay)); // 日志拦截器 输出请求、响应内容 if (kDebugMode) { diff --git a/lib/http/live.dart b/lib/http/live.dart index d9d7131fb..b6c7c16dc 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -16,8 +16,8 @@ import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart'; import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart'; import 'package:PiliPlus/models_new/live/live_search/data.dart'; import 'package:PiliPlus/models_new/live/live_second_list/data.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/app_sign.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; diff --git a/lib/http/member.dart b/lib/http/member.dart index 2bc671278..a9689da2b 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -20,7 +20,7 @@ import 'package:PiliPlus/models_new/space/space_article/data.dart'; import 'package:PiliPlus/models_new/space/space_opus/data.dart'; import 'package:PiliPlus/models_new/space/space_season_series/item.dart'; import 'package:PiliPlus/models_new/upower_rank/data.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 619a89434..85d9f76d3 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -13,7 +13,7 @@ import 'package:PiliPlus/models_new/msg/session_ss/data.dart'; import 'package:PiliPlus/models_new/msgfeed_unread/data.dart'; import 'package:PiliPlus/models_new/single_unread/data.dart'; import 'package:PiliPlus/models_new/upload_bfs/data.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; import 'package:uuid/uuid.dart'; diff --git a/lib/http/pgc.dart b/lib/http/pgc.dart index 89692c6f8..6f431eb37 100644 --- a/lib/http/pgc.dart +++ b/lib/http/pgc.dart @@ -8,7 +8,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_index_result/list.dart'; import 'package:PiliPlus/models_new/pgc/pgc_review/data.dart'; import 'package:PiliPlus/models_new/pgc/pgc_timeline/pgc_timeline.dart'; import 'package:PiliPlus/models_new/pgc/pgc_timeline/result.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:dio/dio.dart'; class PgcHttp { diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 6ad9ed712..627c9400a 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -7,17 +7,14 @@ import 'package:PiliPlus/models_new/emote/package.dart'; import 'package:PiliPlus/models_new/reply/data.dart'; import 'package:PiliPlus/models_new/reply/reply.dart'; import 'package:PiliPlus/models_new/reply2reply/data.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:dio/dio.dart'; class ReplyHttp { static final Options _options = Options(extra: {'account': AnonymousAccount(), 'checkReply': true}); - static RegExp replyRegExp = - RegExp(GStorage.banWordForReply, caseSensitive: false); - static Future replyList({ required bool isLogin, required int oid, @@ -53,68 +50,68 @@ class ReplyHttp { ); if (res.data['code'] == 0) { ReplyData replyData = ReplyData.fromJson(res.data['data']); - if (enableFilter != false && replyRegExp.pattern.isNotEmpty) { - // topReplies - if (replyData.topReplies?.isNotEmpty == true) { - replyData.topReplies!.removeWhere((item) { - bool hasMatch = replyRegExp.hasMatch(item.content?.message ?? ''); - // remove subreplies - if (!hasMatch) { - if (item.replies?.isNotEmpty == true) { - item.replies!.removeWhere((item) => - replyRegExp.hasMatch(item.content?.message ?? '')); - } - } - return hasMatch; - }); - } + // if (enableFilter != false && replyRegExp.pattern.isNotEmpty) { + // // topReplies + // if (replyData.topReplies?.isNotEmpty == true) { + // replyData.topReplies!.removeWhere((item) { + // bool hasMatch = replyRegExp.hasMatch(item.content?.message ?? ''); + // // remove subreplies + // if (!hasMatch) { + // if (item.replies?.isNotEmpty == true) { + // item.replies!.removeWhere((item) => + // replyRegExp.hasMatch(item.content?.message ?? '')); + // } + // } + // return hasMatch; + // }); + // } - // replies - if (replyData.replies?.isNotEmpty == true) { - replyData.replies!.removeWhere((item) { - bool hasMatch = replyRegExp.hasMatch(item.content?.message ?? ''); - // remove subreplies - if (!hasMatch) { - if (item.replies?.isNotEmpty == true) { - item.replies!.removeWhere((item) => - replyRegExp.hasMatch(item.content?.message ?? '')); - } - } - return hasMatch; - }); - } - } + // // replies + // if (replyData.replies?.isNotEmpty == true) { + // replyData.replies!.removeWhere((item) { + // bool hasMatch = replyRegExp.hasMatch(item.content?.message ?? ''); + // // remove subreplies + // if (!hasMatch) { + // if (item.replies?.isNotEmpty == true) { + // item.replies!.removeWhere((item) => + // replyRegExp.hasMatch(item.content?.message ?? '')); + // } + // } + // return hasMatch; + // }); + // } + // } - // antiGoodsReply - if (antiGoodsReply) { - // topReplies - if (replyData.topReplies?.isNotEmpty == true) { - replyData.topReplies!.removeWhere((item) { - bool hasMatch = needRemove(item); - // remove subreplies - if (!hasMatch) { - if (item.replies?.isNotEmpty == true) { - item.replies!.removeWhere(needRemove); - } - } - return hasMatch; - }); - } + // // antiGoodsReply + // if (antiGoodsReply) { + // // topReplies + // if (replyData.topReplies?.isNotEmpty == true) { + // replyData.topReplies!.removeWhere((item) { + // bool hasMatch = needRemove(item); + // // remove subreplies + // if (!hasMatch) { + // if (item.replies?.isNotEmpty == true) { + // item.replies!.removeWhere(needRemove); + // } + // } + // return hasMatch; + // }); + // } - // replies - if (replyData.replies?.isNotEmpty == true) { - replyData.replies!.removeWhere((item) { - bool hasMatch = needRemove(item); - // remove subreplies - if (!hasMatch) { - if (item.replies?.isNotEmpty == true) { - item.replies!.removeWhere(needRemove); - } - } - return hasMatch; - }); - } - } + // // replies + // if (replyData.replies?.isNotEmpty == true) { + // replyData.replies!.removeWhere((item) { + // bool hasMatch = needRemove(item); + // // remove subreplies + // if (!hasMatch) { + // if (item.replies?.isNotEmpty == true) { + // item.replies!.removeWhere(needRemove); + // } + // } + // return hasMatch; + // }); + // } + // } return Success(replyData); } else { return Error(res.data['message']); @@ -161,17 +158,17 @@ class ReplyHttp { ); if (res.data['code'] == 0) { ReplyReplyData replyData = ReplyReplyData.fromJson(res.data['data']); - if (filterBanWord != false && replyRegExp.pattern.isNotEmpty) { - if (replyData.replies?.isNotEmpty == true) { - replyData.replies!.removeWhere( - (item) => replyRegExp.hasMatch(item.content?.message ?? '')); - } - } - if (antiGoodsReply) { - if (replyData.replies?.isNotEmpty == true) { - replyData.replies!.removeWhere(needRemove); - } - } + // if (filterBanWord != false && replyRegExp.pattern.isNotEmpty) { + // if (replyData.replies?.isNotEmpty == true) { + // replyData.replies!.removeWhere( + // (item) => replyRegExp.hasMatch(item.content?.message ?? '')); + // } + // } + // if (antiGoodsReply) { + // if (replyData.replies?.isNotEmpty == true) { + // replyData.replies!.removeWhere(needRemove); + // } + // } return Success(replyData); } else { return Error( diff --git a/lib/http/search.dart b/lib/http/search.dart index 5cf9fb303..b04c2df6d 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -11,7 +11,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_info_model/result.dart'; import 'package:PiliPlus/models_new/search/search_rcmd/data.dart'; import 'package:PiliPlus/models_new/search/search_trending/data.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -79,11 +79,10 @@ class SearchHttp { try { switch (searchType) { case SearchType.video: - Set blackMids = GStorage.blackMids; if (res.data['data']['result'] != null) { for (var i in res.data['data']['result']) { // 屏蔽推广和拉黑用户 - i['available'] = !blackMids.contains(i['mid']); + i['available'] = !GlobalData().blackMids.contains(i['mid']); } } data = SearchVideoData.fromJson(res.data['data']); diff --git a/lib/http/user.dart b/lib/http/user.dart index 9204cda8e..d55d56c45 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -11,8 +11,8 @@ import 'package:PiliPlus/models_new/media_list/data.dart'; import 'package:PiliPlus/models_new/space_setting/data.dart'; import 'package:PiliPlus/models_new/sub/sub/data.dart'; import 'package:PiliPlus/models_new/video/video_tag/data.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/global_data.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; diff --git a/lib/http/validate.dart b/lib/http/validate.dart index 54fc8fa84..69530ca0f 100644 --- a/lib/http/validate.dart +++ b/lib/http/validate.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:dio/dio.dart'; class ValidateHttp { diff --git a/lib/http/video.dart b/lib/http/video.dart index 798339d26..a6b43fcc2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -20,20 +20,22 @@ import 'package:PiliPlus/models_new/video/video_detail/video_detail_response.dar import 'package:PiliPlus/models_new/video/video_note_list/data.dart'; import 'package:PiliPlus/models_new/video/video_play_info/data.dart'; import 'package:PiliPlus/models_new/video/video_relation/data.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/recommend_filter.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; /// view层根据 status 判断渲染逻辑 class VideoHttp { - static bool enableRcmdDynamic = - GStorage.setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true); - static RegExp zoneRegExp = - RegExp(GStorage.banWordForZone, caseSensitive: false); + static bool p1080 = Pref.p1080; + // static bool enableRcmdDynamic = Pref.enableRcmdDynamic; + static RegExp zoneRegExp = RegExp(Pref.banWordForZone, caseSensitive: false); + static bool enableFilter = zoneRegExp.pattern.isNotEmpty; // 首页推荐视频 static Future rcmdVideoList( @@ -52,11 +54,11 @@ class VideoHttp { ); if (res.data['code'] == 0) { List list = []; - Set blackMids = GStorage.blackMids; for (var i in res.data['data']['item']) { //过滤掉live与ad,以及拉黑用户 if (i['goto'] == 'av' && - (i['owner'] != null && !blackMids.contains(i['owner']['mid']))) { + (i['owner'] != null && + !GlobalData().blackMids.contains(i['owner']['mid']))) { RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i); if (!RecommendFilter.filter(videoItem)) { list.add(videoItem); @@ -121,15 +123,15 @@ class VideoHttp { ); if (res.data['code'] == 0) { List list = []; - Set blackMids = GStorage.blackMids; for (var i in res.data['data']['items']) { // 屏蔽推广和拉黑用户 if (i['card_goto'] != 'ad_av' && i['card_goto'] != 'ad_web_s' && i['ad_info'] == null && - (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && - (i['args'] != null && !blackMids.contains(i['args']['up_id']))) { - if (zoneRegExp.pattern.isNotEmpty && + // (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && + (i['args'] != null && + !GlobalData().blackMids.contains(i['args']['up_id']))) { + if (enableFilter && i['args']?['tname'] != null && zoneRegExp.hasMatch(i['args']['tname'])) { continue; @@ -155,13 +157,12 @@ class VideoHttp { ); if (res.data['code'] == 0) { List list = []; - Set blackMids = GStorage.blackMids; for (var i in res.data['data']['list']) { - if (!blackMids.contains(i['owner']['mid']) && + if (!GlobalData().blackMids.contains(i['owner']['mid']) && !RecommendFilter.filterTitle(i['title']) && !RecommendFilter.filterLikeRatio( i['stat']['like'], i['stat']['view'])) { - if (zoneRegExp.pattern.isNotEmpty && + if (enableFilter && i['tname'] != null && zoneRegExp.hasMatch(i['tname'])) { continue; @@ -201,9 +202,7 @@ class VideoHttp { 'isGaiaAvoided': true, 'web_location': 1315873, // 免登录查看1080p - if (!Accounts.get(AccountType.video).isLogin && - GStorage.setting.get(SettingBoxKey.p1080, defaultValue: true)) - 'try_look': 1, + if (!Accounts.get(AccountType.video).isLogin && p1080) 'try_look': 1, }); late final usePgcApi = @@ -814,13 +813,12 @@ class VideoHttp { ); if (res.data['code'] == 0) { List list = []; - Set blackMids = GStorage.blackMids; for (var i in res.data['data']['list']) { - if (!blackMids.contains(i['owner']['mid']) && + if (!GlobalData().blackMids.contains(i['owner']['mid']) && !RecommendFilter.filterTitle(i['title']) && !RecommendFilter.filterLikeRatio( i['stat']['like'], i['stat']['view'])) { - if (zoneRegExp.pattern.isNotEmpty && + if (enableFilter && i['tname'] != null && zoneRegExp.hasMatch(i['tname'])) { continue; diff --git a/lib/main.dart b/lib/main.dart index 7423e5d76..ea1498f19 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,8 +14,9 @@ import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/cache_manage.dart'; import 'package:PiliPlus/utils/data.dart'; import 'package:PiliPlus/utils/date_util.dart'; -import 'package:PiliPlus/utils/recommend_filter.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/theme_utils.dart'; import 'package:catcher_2/catcher_2.dart'; import 'package:dynamic_color/dynamic_color.dart'; @@ -27,7 +28,6 @@ import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. void main() async { @@ -35,10 +35,10 @@ void main() async { MediaKit.ensureInitialized(); await GStorage.init(); Get.put(AccountService()); - if (GStorage.setting.get(SettingBoxKey.autoClearCache, defaultValue: false)) { + if (Pref.autoClearCache) { await CacheManage.clearLibraryCache(); } else { - final num maxCacheSize = GStorage.maxCacheSize; + final num maxCacheSize = Pref.maxCacheSize; if (maxCacheSize != 0) { final double currCache = await CacheManage().loadApplicationCache(); if (currCache >= maxCacheSize) { @@ -46,7 +46,7 @@ void main() async { } } } - if (GStorage.horizontalScreen) { + if (Pref.horizontalScreen) { await SystemChrome.setPreferredOrientations( //支持竖屏与横屏 [ @@ -68,9 +68,8 @@ void main() async { await setupServiceLocator(); Request(); await Request.setCookie(); - RecommendFilter(); - if (GStorage.enableLog) { + if (Pref.enableLog) { // 异常捕获 logo记录 String buildConfig = '''\n Build Time: ${DateUtil.format(BuildConfig.buildTime, format: DateUtil.longFormatDs)} @@ -129,34 +128,20 @@ Commit Hash: ${BuildConfig.commitHash}'''; class MyApp extends StatelessWidget { const MyApp({super.key}); - Box get setting => GStorage.setting; - static ThemeData? darkThemeData; @override Widget build(BuildContext context) { - // 主题色 - Color defaultColor = - colorThemeTypes[setting.get(SettingBoxKey.customColor, defaultValue: 0)] - .color; - Color brandColor = defaultColor; - // 是否动态取色 - bool isDynamicColor = - setting.get(SettingBoxKey.dynamicColor, defaultValue: true); - // 字体缩放大小 - double textScale = - setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0); - // DynamicSchemeVariant dynamicSchemeVariant = - // DynamicSchemeVariant.values[GStorage.schemeVariant]; - FlexSchemeVariant variant = - FlexSchemeVariant.values[GStorage.schemeVariant]; + Color brandColor = colorThemeTypes[Pref.customColor].color; + bool isDynamicColor = Pref.dynamicColor; + FlexSchemeVariant variant = FlexSchemeVariant.values[Pref.schemeVariant]; // 强制设置高帧率 if (Platform.isAndroid) { late List modes; FlutterDisplayMode.supported.then((value) { modes = value; - var storageDisplay = setting.get(SettingBoxKey.displayMode); + var storageDisplay = GStorage.setting.get(SettingBoxKey.displayMode); DisplayMode f = DisplayMode.auto; if (storageDisplay != null) { f = modes.firstWhere((e) => e.toString() == storageDisplay, @@ -209,7 +194,7 @@ class MyApp extends StatelessWidget { isDark: true, variant: variant, ), - themeMode: GStorage.themeMode, + themeMode: Pref.themeMode, localizationsDelegates: const [ GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, @@ -225,8 +210,8 @@ class MyApp extends StatelessWidget { loadingBuilder: (msg) => LoadingWidget(msg: msg), builder: (context, child) { return MediaQuery( - data: MediaQuery.of(context) - .copyWith(textScaler: TextScaler.linear(textScale)), + data: MediaQuery.of(context).copyWith( + textScaler: TextScaler.linear(Pref.defaultTextScale)), child: child!, ); }, @@ -243,7 +228,7 @@ class MyApp extends StatelessWidget { } class _CustomHttpOverrides extends HttpOverrides { - final badCertificateCallback = kDebugMode || GStorage.badCertificateCallback; + final badCertificateCallback = kDebugMode || Pref.badCertificateCallback; @override HttpClient createHttpClient(SecurityContext? context) { diff --git a/lib/models/common/dynamic/dynamic_badge_mode.dart b/lib/models/common/dynamic/dynamic_badge_mode.dart index f76a2b1e1..9ce018d6b 100644 --- a/lib/models/common/dynamic/dynamic_badge_mode.dart +++ b/lib/models/common/dynamic/dynamic_badge_mode.dart @@ -1,5 +1,9 @@ -enum DynamicBadgeMode { hidden, point, number } +enum DynamicBadgeMode { + hidden('隐藏'), + point('红点'), + number('数字'), + ; -extension DynamicBadgeModeExt on DynamicBadgeMode { - String get description => const ['隐藏', '红点', '数字'][index]; + final String desc; + const DynamicBadgeMode(this.desc); } diff --git a/lib/models/common/dynamic/dynamics_type.dart b/lib/models/common/dynamic/dynamics_type.dart index 5829cd89e..a2c397422 100644 --- a/lib/models/common/dynamic/dynamics_type.dart +++ b/lib/models/common/dynamic/dynamics_type.dart @@ -1,12 +1,11 @@ enum DynamicsTabType { - all, - video, - pgc, - article, - up, -} + all('全部'), + video('投稿'), + pgc('番剧'), + article('专栏'), + up('UP'), + ; -extension DynamicsTabTypeExt on DynamicsTabType { - String get values => const ['all', 'video', 'pgc', 'article', 'up'][index]; - String get labels => const ['全部', '投稿', '番剧', '专栏', 'UP'][index]; + final String label; + const DynamicsTabType(this.label); } diff --git a/lib/models/common/member/tab_type.dart b/lib/models/common/member/tab_type.dart index 1f1e3e08d..53a9fe950 100644 --- a/lib/models/common/member/tab_type.dart +++ b/lib/models/common/member/tab_type.dart @@ -1,5 +1,12 @@ -enum MemberTabType { none, home, dynamic, contribute, favorite, bangumi } +enum MemberTabType { + def('默认'), + home('主页'), + dynamic('动态'), + contribute('投稿'), + favorite('收藏'), + bangumi('番剧'), + ; -extension MemberTabTypeExt on MemberTabType { - String get title => const ['默认', '主页', '动态', '投稿', '收藏', '番剧'][index]; + final String title; + const MemberTabType(this.title); } diff --git a/lib/models/common/msg/msg_type.dart b/lib/models/common/msg/msg_type.dart index f26389b4f..2076fe6bc 100644 --- a/lib/models/common/msg/msg_type.dart +++ b/lib/models/common/msg/msg_type.dart @@ -1,27 +1,27 @@ -enum MsgType { - invalid(value: 0, label: "空空的~"), - text(value: 1, label: "文本消息"), - pic(value: 2, label: "图片消息"), - audio(value: 3, label: "语音消息"), - share(value: 4, label: "分享消息"), - revoke(value: 5, label: "撤回消息"), - customFace(value: 6, label: "自定义表情"), - shareV2(value: 7, label: "分享v2消息"), - sysCancel(value: 8, label: "系统撤销"), - miniProgram(value: 9, label: "小程序"), - notifyMsg(value: 10, label: "业务通知"), - archiveCard(value: 11, label: "投稿卡片"), - articleCard(value: 12, label: "专栏卡片"), - picCard(value: 13, label: "图片卡片"), - commonShare(value: 14, label: "异形卡片"), - autoReplyPush(value: 16, label: "自动回复推送"), - notifyText(value: 18, label: "文本提示"); +// enum MsgType { +// invalid(value: 0, label: "空空的~"), +// text(value: 1, label: "文本消息"), +// pic(value: 2, label: "图片消息"), +// audio(value: 3, label: "语音消息"), +// share(value: 4, label: "分享消息"), +// revoke(value: 5, label: "撤回消息"), +// customFace(value: 6, label: "自定义表情"), +// shareV2(value: 7, label: "分享v2消息"), +// sysCancel(value: 8, label: "系统撤销"), +// miniProgram(value: 9, label: "小程序"), +// notifyMsg(value: 10, label: "业务通知"), +// archiveCard(value: 11, label: "投稿卡片"), +// articleCard(value: 12, label: "专栏卡片"), +// picCard(value: 13, label: "图片卡片"), +// commonShare(value: 14, label: "异形卡片"), +// autoReplyPush(value: 16, label: "自动回复推送"), +// notifyText(value: 18, label: "文本提示"); - final int value; - final String label; - const MsgType({required this.value, required this.label}); - static MsgType parse(int value) { - return MsgType.values - .firstWhere((e) => e.value == value, orElse: () => MsgType.invalid); - } -} +// final int value; +// final String label; +// const MsgType({required this.value, required this.label}); +// static MsgType parse(int value) { +// return MsgType.values +// .firstWhere((e) => e.value == value, orElse: () => MsgType.invalid); +// } +// } diff --git a/lib/models/common/reply/reply_option_type.dart b/lib/models/common/reply/reply_option_type.dart index 1f8694fda..e5a6e988b 100644 --- a/lib/models/common/reply/reply_option_type.dart +++ b/lib/models/common/reply/reply_option_type.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; -enum ReplyOptionType { allow, close, choose } +enum ReplyOptionType { + allow('允许评论'), + close('关闭评论'), + choose('精选评论'), + ; -extension ReplyOptionTypeExt on ReplyOptionType { - String get title => const ['允许评论', '关闭评论', '精选评论'][index]; + final String title; + const ReplyOptionType(this.title); IconData get iconData => switch (this) { ReplyOptionType.allow => MdiIcons.commentTextOutline, diff --git a/lib/models/common/reply/reply_sort_type.dart b/lib/models/common/reply/reply_sort_type.dart index 86ce382d2..392515c1e 100644 --- a/lib/models/common/reply/reply_sort_type.dart +++ b/lib/models/common/reply/reply_sort_type.dart @@ -1,6 +1,8 @@ -enum ReplySortType { time, like } +enum ReplySortType { + time('最新评论', '最新'), + hot('最热评论', '最热'); -extension ReplySortTypeExt on ReplySortType { - String get title => const ['最新评论', '最热评论'][index]; - String get label => const ['最新', '最热'][index]; + final String title; + final String label; + const ReplySortType(this.title, this.label); } diff --git a/lib/models/common/search_type.dart b/lib/models/common/search_type.dart index 513635830..6e8c77d66 100644 --- a/lib/models/common/search_type.dart +++ b/lib/models/common/search_type.dart @@ -38,6 +38,6 @@ enum ArchiveFilterType { // 专栏 // attention('最多喜欢'), - final String description; - const ArchiveFilterType(this.description); + final String desc; + const ArchiveFilterType(this.desc); } diff --git a/lib/models/common/settings_type.dart b/lib/models/common/settings_type.dart index 5a7e30538..5cd6a19e6 100644 --- a/lib/models/common/settings_type.dart +++ b/lib/models/common/settings_type.dart @@ -1 +1 @@ -enum SettingsType { normal, sw1tch, divider } +enum SettingsType { normal, sw1tch } diff --git a/lib/models/common/sponsor_block/action_type.dart b/lib/models/common/sponsor_block/action_type.dart index 57740afe7..6a73a1337 100644 --- a/lib/models/common/sponsor_block/action_type.dart +++ b/lib/models/common/sponsor_block/action_type.dart @@ -1,5 +1,10 @@ -enum ActionType { skip, mute, full, poi } +enum ActionType { + skip('跳过'), + mute('静音'), + full('整个视频'), + poi('精彩时刻'), + ; -extension ActionTypeExt on ActionType { - String get title => const ['跳过', '静音', '整个视频', '精彩时刻'][index]; + final String title; + const ActionType(this.title); } diff --git a/lib/models/common/sponsor_block/skip_type.dart b/lib/models/common/sponsor_block/skip_type.dart index 6c4cd3981..6fd9a4c51 100644 --- a/lib/models/common/sponsor_block/skip_type.dart +++ b/lib/models/common/sponsor_block/skip_type.dart @@ -1,5 +1,11 @@ -enum SkipType { alwaysSkip, skipOnce, skipManually, showOnly, disable } +enum SkipType { + alwaysSkip('总是跳过'), + skipOnce('跳过一次'), + skipManually('手动跳过'), + showOnly('仅显示'), + disable('禁用'), + ; -extension SkipTypeExt on SkipType { - String get title => const ['总是跳过', '跳过一次', '手动跳过', '仅显示', '禁用'][index]; + final String title; + const SkipType(this.title); } diff --git a/lib/models/common/theme/theme_type.dart b/lib/models/common/theme/theme_type.dart index 62e2916cd..ffa91a681 100644 --- a/lib/models/common/theme/theme_type.dart +++ b/lib/models/common/theme/theme_type.dart @@ -2,15 +2,13 @@ import 'package:flutter/material.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; enum ThemeType { - light, - dark, - system, -} + light('浅色'), + dark('深色'), + system('跟随系统'), + ; -extension ThemeTypeExt on ThemeType { - String get description => const ['浅色', '深色', '跟随系统'][index]; - - int get code => index; + final String desc; + const ThemeType(this.desc); ThemeMode get toThemeMode => switch (this) { ThemeType.light => ThemeMode.light, diff --git a/lib/models/common/video/audio_quality.dart b/lib/models/common/video/audio_quality.dart index 3bf5d5646..3398a9329 100644 --- a/lib/models/common/video/audio_quality.dart +++ b/lib/models/common/video/audio_quality.dart @@ -6,9 +6,9 @@ enum AudioQuality { hiRes(30251, 'Hi-Res无损'); final int code; - final String description; + final String desc; - const AudioQuality(this.code, this.description); + const AudioQuality(this.code, this.desc); static final _codeMap = {for (var i in values) i.code: i}; diff --git a/lib/models/common/video/cdn_type.dart b/lib/models/common/video/cdn_type.dart index e405bdaac..238b6bc5e 100644 --- a/lib/models/common/video/cdn_type.dart +++ b/lib/models/common/video/cdn_type.dart @@ -29,8 +29,8 @@ enum CDNService { String get code => name; static final fromCode = values.byName; - final String description; + final String desc; final String host; - const CDNService(this.description, [this.host = '']); + const CDNService(this.desc, [this.host = '']); } diff --git a/lib/models/common/video/video_quality.dart b/lib/models/common/video/video_quality.dart index e5883a752..362ceaaad 100644 --- a/lib/models/common/video/video_quality.dart +++ b/lib/models/common/video/video_quality.dart @@ -13,9 +13,9 @@ enum VideoQuality { super8k(127, '8K 超高清'); final int code; - final String description; + final String desc; - const VideoQuality(this.code, this.description); + const VideoQuality(this.code, this.desc); static final _codeMap = {for (var i in values) i.code: i}; diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index dcd3ceaa2..92ca22ec8 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; import 'package:PiliPlus/models/dynamics/article_content_model.dart'; import 'package:PiliPlus/models/model_avatar.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/watched_show.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; class DynamicsDataModel { bool? hasMore; @@ -14,7 +14,10 @@ class DynamicsDataModel { int? total; static RegExp banWordForDyn = - RegExp(GStorage.banWordForDyn, caseSensitive: false); + RegExp(Pref.banWordForDyn, caseSensitive: false); + static bool enableFilter = banWordForDyn.pattern.isNotEmpty; + + static bool antiGoodsDyn = Pref.antiGoodsDyn; DynamicsDataModel.fromJson( Map json, { @@ -26,8 +29,6 @@ class DynamicsDataModel { List? list = json['items'] as List?; if (list != null && list.isNotEmpty) { items = []; - late final antiGoodsDyn = GStorage.antiGoodsDyn; - late final filterWord = banWordForDyn.pattern.isNotEmpty; late final filterBan = type != DynamicsTabType.up && tempBannedList?.isNotEmpty == true; for (var e in list) { @@ -39,7 +40,7 @@ class DynamicsDataModel { 'ADDITIONAL_TYPE_GOODS')) { continue; } - if (filterWord && + if (enableFilter && banWordForDyn.hasMatch( item.orig?.modules.moduleDynamic?.major?.opus?.summary?.text ?? item.modules.moduleDynamic?.major?.opus?.summary?.text ?? diff --git a/lib/models/video/play/url.dart b/lib/models/video/play/url.dart index 10899c7f6..9aa5f4563 100644 --- a/lib/models/video/play/url.dart +++ b/lib/models/video/play/url.dart @@ -219,7 +219,7 @@ class AudioItem extends BaseItem { AudioItem(); AudioItem.fromJson(Map json) : super.fromJson(json) { - quality = AudioQuality.fromCode(json['id']).description; + quality = AudioQuality.fromCode(json['id']).desc; } } diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index 5eb1f281f..800b9568d 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/services/loggeer.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/cache_manage.dart'; import 'package:PiliPlus/utils/date_util.dart'; diff --git a/lib/pages/article/controller.dart b/lib/pages/article/controller.dart index e85ebd4b4..c204e1924 100644 --- a/lib/pages/article/controller.dart +++ b/lib/pages/article/controller.dart @@ -13,7 +13,7 @@ import 'package:PiliPlus/models_new/article/article_info/data.dart'; import 'package:PiliPlus/models_new/article/article_view/data.dart'; import 'package:PiliPlus/pages/common/reply_controller.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/url_utils.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -31,8 +31,8 @@ class ArticleController extends ReplyController { late final RxInt topIndex = 0.obs; - late final horizontalPreview = GStorage.horizontalPreview; - late final showDynActionBar = GStorage.showDynActionBar; + late final horizontalPreview = Pref.horizontalPreview; + late final showDynActionBar = Pref.showDynActionBar; @override dynamic get sourceId => commentType == 12 ? 'cv$commentId' : id; @@ -180,7 +180,6 @@ class ArticleController extends ReplyController { mode: mode.value, cursorNext: cursorNext, offset: paginationReply?.nextOffset, - antiGoodsReply: antiGoodsReply, ); } diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index 67f47cea6..0c6a0de45 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -13,7 +13,6 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; -import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; import 'package:PiliPlus/models/dynamics/result.dart' show DynamicStat; import 'package:PiliPlus/pages/article/controller.dart'; import 'package:PiliPlus/pages/article/widgets/article_ops.dart'; @@ -30,6 +29,8 @@ import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; @@ -59,7 +60,7 @@ class _ArticlePageState extends State late final AnimationController fabAnimationCtr; late final Animation _anim; - late final List _ratio = GStorage.dynamicDetailRatio; + late final List _ratio = Pref.dynamicDetailRatio; bool get _horizontalPreview => context.orientation == Orientation.landscape && @@ -709,7 +710,7 @@ class _ArticlePageState extends State value: _ratio.first, onChanged: (value) { if (value >= 10 && value <= 90) { - _ratio[0] = value; + _ratio[0] = value.toPrecision(2); _ratio[1] = 100 - value; GStorage.setting.put( SettingBoxKey.dynamicDetailRatio, diff --git a/lib/pages/blacklist/view.dart b/lib/pages/blacklist/view.dart index e4b17ebbf..f80160b23 100644 --- a/lib/pages/blacklist/view.dart +++ b/lib/pages/blacklist/view.dart @@ -7,7 +7,7 @@ import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models_new/blacklist/list.dart'; import 'package:PiliPlus/pages/blacklist/controller.dart'; import 'package:PiliPlus/utils/date_util.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -24,7 +24,7 @@ class _BlackListPageState extends State { @override void dispose() { if (_blackListController.loadingState.value.isSuccess) { - GStorage.blackMids = _blackListController.loadingState.value.data! + Pref.blackMids = _blackListController.loadingState.value.data! .map((e) => e.mid!) .toSet(); } diff --git a/lib/pages/common/common_intro_controller.dart b/lib/pages/common/common_intro_controller.dart index 2b82b2812..a5d020a5c 100644 --- a/lib/pages/common/common_intro_controller.dart +++ b/lib/pages/common/common_intro_controller.dart @@ -3,7 +3,7 @@ import 'package:PiliPlus/models_new/fav/fav_folder/data.dart'; import 'package:PiliPlus/models_new/video/video_tag/data.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -31,8 +31,7 @@ abstract class CommonIntroController extends GetxController { Future actionFavVideo({String type = 'choose'}); - late final enableQuickFav = - GStorage.setting.get(SettingBoxKey.enableQuickFav, defaultValue: false); + late final enableQuickFav = Pref.enableQuickFav; // 收藏 void showFavBottomSheet(BuildContext context, {type = 'tap'}) { diff --git a/lib/pages/common/common_slide_page.dart b/lib/pages/common/common_slide_page.dart index 6c9cfa416..7e781c161 100644 --- a/lib/pages/common/common_slide_page.dart +++ b/lib/pages/common/common_slide_page.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -19,10 +19,12 @@ abstract class CommonSlidePageState extends State AnimationController? _animController; Animation? _anim; + static bool slideDismissReplyPage = Pref.slideDismissReplyPage; + @override void initState() { super.initState(); - enableSlide = widget.enableSlide != false && GStorage.slideDismissReplyPage; + enableSlide = widget.enableSlide != false && slideDismissReplyPage; if (enableSlide) { _animController = AnimationController( vsync: this, diff --git a/lib/pages/common/common_whisper_controller.dart b/lib/pages/common/common_whisper_controller.dart index 8755c7084..1b830d3de 100644 --- a/lib/pages/common/common_whisper_controller.dart +++ b/lib/pages/common/common_whisper_controller.dart @@ -4,7 +4,7 @@ import 'package:PiliPlus/grpc/im.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; diff --git a/lib/pages/common/reply_controller.dart b/lib/pages/common/reply_controller.dart index 8f47186b7..e2d7f8a95 100644 --- a/lib/pages/common/reply_controller.dart +++ b/lib/pages/common/reply_controller.dart @@ -10,7 +10,7 @@ import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/reply_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; @@ -21,7 +21,8 @@ import 'package:get/get_navigation/src/dialog/dialog_route.dart'; abstract class ReplyController extends CommonListController { RxInt count = (-1).obs; - Rx sortType = ReplySortType.time.obs; + late Rx sortType; + late Rx mode; late final savedReplies = {}; @@ -31,17 +32,14 @@ abstract class ReplyController extends CommonListController { Int64? cursorNext; SubjectControl? subjectControl; FeedPaginationReply? paginationReply; - late Rx mode = Mode.MAIN_LIST_HOT.obs; late bool hasUpTop = false; @override bool? get hasFooter => true; - late final antiGoodsReply = GStorage.antiGoodsReply; - // comment antifraud - late final _enableCommAntifraud = GStorage.enableCommAntifraud; - late final _biliSendCommAntifraud = GStorage.biliSendCommAntifraud; + late final _enableCommAntifraud = Pref.enableCommAntifraud; + late final _biliSendCommAntifraud = Pref.biliSendCommAntifraud; bool get enableCommAntifraud => _enableCommAntifraud || _biliSendCommAntifraud; dynamic get sourceId; @@ -49,16 +47,9 @@ abstract class ReplyController extends CommonListController { @override void onInit() { super.onInit(); - int defaultReplySortIndex = GStorage.setting - .get(SettingBoxKey.replySortType, defaultValue: 1) as int; - if (defaultReplySortIndex == 2) { - GStorage.setting.put(SettingBoxKey.replySortType, 0); - defaultReplySortIndex = 0; - } - sortType.value = ReplySortType.values[defaultReplySortIndex]; - if (sortType.value == ReplySortType.time) { - mode.value = Mode.MAIN_LIST_TIME; - } + int replySortType = Pref.replySortType; + sortType = ReplySortType.values[replySortType].obs; + mode = (replySortType == 0 ? Mode.MAIN_LIST_TIME : Mode.MAIN_LIST_HOT).obs; } @override @@ -100,10 +91,10 @@ abstract class ReplyController extends CommonListController { feedBack(); switch (sortType.value) { case ReplySortType.time: - sortType.value = ReplySortType.like; + sortType.value = ReplySortType.hot; mode.value = Mode.MAIN_LIST_HOT; break; - case ReplySortType.like: + case ReplySortType.hot: sortType.value = ReplySortType.time; mode.value = Mode.MAIN_LIST_TIME; break; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 3351e0dde..b75dd8bf3 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -6,7 +6,6 @@ import 'package:PiliPlus/pages/danmaku/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; import 'package:PiliPlus/utils/danmaku_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -33,7 +32,6 @@ class _PlDanmakuState extends State { late PlDanmakuController _plDanmakuController; DanmakuController? _controller; - late bool enableShowDanmaku; int latestAddedPosition = -1; bool? _isFullScreen; StreamSubscription? _listenerFS; @@ -41,13 +39,11 @@ class _PlDanmakuState extends State { @override void initState() { super.initState(); - enableShowDanmaku = GStorage.setting - .get(SettingBoxKey.enableShowDanmaku, defaultValue: true); _plDanmakuController = PlDanmakuController( widget.cid, playerController, ); - if (enableShowDanmaku || playerController.isOpenDanmu.value) { + if (playerController.enableShowDanmaku.value) { _plDanmakuController.queryDanmaku( _plDanmakuController.calcSegment( playerController.position.value.inMilliseconds, @@ -81,7 +77,7 @@ class _PlDanmakuState extends State { } void videoPositionListen(Duration position) { - if (!playerController.isOpenDanmu.value) { + if (!playerController.enableShowDanmaku.value) { return; } @@ -151,8 +147,8 @@ class _PlDanmakuState extends State { double _getFontSize(bool isFullScreen) => !isFullScreen || widget.isPipMode == true - ? 15 * playerController.fontSize - : 15 * playerController.fontSizeFS; + ? 15 * playerController.danmakuFontScale + : 15 * playerController.danmakuFontScaleFS; @override Widget build(BuildContext context) { @@ -160,7 +156,7 @@ class _PlDanmakuState extends State { // double initDuration = box.maxWidth / 12; return Obx( () => AnimatedOpacity( - opacity: playerController.isOpenDanmu.value ? 1 : 0, + opacity: playerController.enableShowDanmaku.value ? 1 : 0, duration: const Duration(milliseconds: 100), child: DanmakuScreen( createdController: (DanmakuController e) { @@ -170,7 +166,7 @@ class _PlDanmakuState extends State { fontSize: _getFontSize(playerController.isFullScreen.value), fontWeight: playerController.fontWeight, area: playerController.showArea, - opacity: playerController.opacity, + opacity: playerController.danmakuOpacity, hideTop: playerController.blockTypes.contains(5), hideScroll: playerController.blockTypes.contains(2), hideBottom: playerController.blockTypes.contains(4), diff --git a/lib/pages/danmaku_block/view.dart b/lib/pages/danmaku_block/view.dart index 0ae43814d..1d43952b5 100644 --- a/lib/pages/danmaku_block/view.dart +++ b/lib/pages/danmaku_block/view.dart @@ -8,6 +8,7 @@ import 'package:PiliPlus/models/user/danmaku_rule.dart'; import 'package:PiliPlus/pages/danmaku_block/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 86638bf33..94e390926 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -10,7 +10,7 @@ import 'package:PiliPlus/pages/common/common_controller.dart'; import 'package:PiliPlus/pages/dynamics_tab/controller.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -31,9 +31,9 @@ class DynamicsController extends GetxController int allFollowedUpsTotal = 0; late int currentMid = -1; - late bool showLiveItems = GStorage.expandDynLivePanel; + late bool showLiveItems = Pref.expandDynLivePanel; - final upPanelPosition = GStorage.upPanelPosition; + final upPanelPosition = Pref.upPanelPosition; AccountService accountService = Get.find(); @@ -52,8 +52,7 @@ class DynamicsController extends GetxController tabController = TabController( length: DynamicsTabType.values.length, vsync: this, - initialIndex: GStorage.setting - .get(SettingBoxKey.defaultDynamicType, defaultValue: 0), + initialIndex: Pref.defaultDynamicType, ); queryFollowUp(); @@ -102,8 +101,7 @@ class DynamicsController extends GetxController ..refresh(); } upData.value.errMsg = null; - if (GStorage.setting - .get(SettingBoxKey.dynamicsShowAllFollowedUp, defaultValue: false)) { + if (Pref.dynamicsShowAllFollowedUp) { allFollowedUpsPage = 1; final f1 = DynamicsHttp.followUp(); final f2 = FollowHttp.followings( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 45833f9ab..77cefe8e7 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/pages/dynamics/controller.dart'; import 'package:PiliPlus/pages/dynamics/widgets/up_panel.dart'; import 'package:PiliPlus/pages/dynamics_create/view.dart'; import 'package:PiliPlus/pages/dynamics_tab/view.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart' hide DraggableScrollableSheet; import 'package:get/get.dart'; @@ -56,8 +56,7 @@ class _DynamicsPageState extends State @override void initState() { super.initState(); - if (GStorage.setting - .get(SettingBoxKey.dynamicsShowAllFollowedUp, defaultValue: false)) { + if (Pref.dynamicsShowAllFollowedUp) { _dynamicsController.scrollController.addListener(listener); } } @@ -135,7 +134,7 @@ class _DynamicsPageState extends State TabBarTheme.of(context).labelStyle?.copyWith(fontSize: 13) ?? const TextStyle(fontSize: 13), tabs: - DynamicsTabType.values.map((e) => Tab(text: e.labels)).toList(), + DynamicsTabType.values.map((e) => Tab(text: e.label)).toList(), onTap: (index) { if (!_dynamicsController.tabController.indexIsChanging) { _dynamicsController.animateToTop(); diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index bb373ae56..355906504 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -8,12 +8,12 @@ import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/controller.dart'; import 'package:PiliPlus/pages/save_panel/view.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; diff --git a/lib/pages/dynamics_create/view.dart b/lib/pages/dynamics_create/view.dart index 1e9eca00a..2979f6e74 100644 --- a/lib/pages/dynamics_create/view.dart +++ b/lib/pages/dynamics_create/view.dart @@ -18,9 +18,9 @@ import 'package:PiliPlus/pages/dynamics_select_topic/controller.dart'; import 'package:PiliPlus/pages/dynamics_select_topic/view.dart'; import 'package:PiliPlus/pages/emote/controller.dart'; import 'package:PiliPlus/pages/emote/view.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart' hide DraggableScrollableSheet; import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; diff --git a/lib/pages/dynamics_detail/controller.dart b/lib/pages/dynamics_detail/controller.dart index 90f2a7a33..f6c6a0df7 100644 --- a/lib/pages/dynamics_detail/controller.dart +++ b/lib/pages/dynamics_detail/controller.dart @@ -6,7 +6,7 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/common/reply_controller.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -15,8 +15,8 @@ class DynamicDetailController extends ReplyController { late int replyType; late DynamicItemModel dynItem; - late final horizontalPreview = GStorage.horizontalPreview; - late final showDynActionBar = GStorage.showDynActionBar; + late final horizontalPreview = Pref.horizontalPreview; + late final showDynActionBar = Pref.showDynActionBar; @override dynamic get sourceId => replyType == 1 ? IdUtils.av2bv(oid) : oid; @@ -61,6 +61,5 @@ class DynamicDetailController extends ReplyController { mode: mode.value, cursorNext: cursorNext, offset: paginationReply?.nextOffset, - antiGoodsReply: antiGoodsReply, ); } diff --git a/lib/pages/dynamics_detail/view.dart b/lib/pages/dynamics_detail/view.dart index 7f0e482ea..67719c066 100644 --- a/lib/pages/dynamics_detail/view.dart +++ b/lib/pages/dynamics_detail/view.dart @@ -9,7 +9,6 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; @@ -23,6 +22,8 @@ import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; @@ -48,7 +49,7 @@ class _DynamicDetailPageState extends State bool _isFabVisible = true; bool? _imageStatus; - late final List _ratio = GStorage.dynamicDetailRatio; + late final List _ratio = Pref.dynamicDetailRatio; bool get _horizontalPreview => context.orientation == Orientation.landscape && @@ -285,7 +286,7 @@ class _DynamicDetailPageState extends State value: _ratio.first, onChanged: (value) { if (value >= 10 && value <= 90) { - _ratio[0] = value; + _ratio[0] = value.toPrecision(2); _ratio[1] = 100 - value; GStorage.setting.put( SettingBoxKey.dynamicDetailRatio, diff --git a/lib/pages/dynamics_repost/view.dart b/lib/pages/dynamics_repost/view.dart index 7838dd38a..91ff06c1a 100644 --- a/lib/pages/dynamics_repost/view.dart +++ b/lib/pages/dynamics_repost/view.dart @@ -8,8 +8,8 @@ import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/common/common_publish_page.dart'; import 'package:PiliPlus/pages/emote/controller.dart'; import 'package:PiliPlus/pages/emote/view.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart' hide DraggableScrollableSheet; import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; diff --git a/lib/pages/dynamics_tab/view.dart b/lib/pages/dynamics_tab/view.dart index 0d10a816b..4bf2b8eb1 100644 --- a/lib/pages/dynamics_tab/view.dart +++ b/lib/pages/dynamics_tab/view.dart @@ -13,8 +13,8 @@ import 'package:PiliPlus/pages/dynamics/controller.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; import 'package:PiliPlus/pages/dynamics_tab/controller.dart'; import 'package:PiliPlus/pages/main/controller.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:waterfall_flow/waterfall_flow.dart'; @@ -66,7 +66,6 @@ class DynamicsTabPage extends CommonPage { class _DynamicsTabPageState extends CommonPageState with AutomaticKeepAliveClientMixin { - late bool dynamicsWaterfallFlow; StreamSubscription? _listener; late final MainController _mainController = Get.find(); @@ -102,8 +101,6 @@ class _DynamicsTabPageState } }); } - dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); } @override @@ -138,9 +135,10 @@ class _DynamicsTabPageState Widget _buildBody(LoadingState?> loadingState) { return switch (loadingState) { - Loading() => DynamicsTabPage.dynSkeleton(dynamicsWaterfallFlow), + Loading() => + DynamicsTabPage.dynSkeleton(GlobalData().dynamicsWaterfallFlow), Success(:var response) => response?.isNotEmpty == true - ? dynamicsWaterfallFlow + ? GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow.extent( maxCrossAxisExtent: Grid.smallCardWidth * 2, crossAxisSpacing: StyleString.cardSpace / 2, diff --git a/lib/pages/dynamics_topic/view.dart b/lib/pages/dynamics_topic/view.dart index cac15290a..2ff926fd5 100644 --- a/lib/pages/dynamics_topic/view.dart +++ b/lib/pages/dynamics_topic/view.dart @@ -14,10 +14,10 @@ import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; import 'package:PiliPlus/pages/dynamics_create/view.dart'; import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/dynamics_topic/controller.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -36,8 +36,6 @@ class DynTopicPage extends StatefulWidget { class _DynTopicPageState extends State { final DynTopicController _controller = Get.put(DynTopicController(), tag: Utils.generateRandomString(8)); - final dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); @override Widget build(BuildContext context) { @@ -329,9 +327,10 @@ class _DynTopicPageState extends State { Widget _buildBody(LoadingState?> loadingState) { return switch (loadingState) { - Loading() => DynamicsTabPage.dynSkeleton(dynamicsWaterfallFlow), + Loading() => + DynamicsTabPage.dynSkeleton(GlobalData().dynamicsWaterfallFlow), Success(:var response) => response?.isNotEmpty == true - ? dynamicsWaterfallFlow + ? GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow.extent( maxCrossAxisExtent: Grid.smallCardWidth * 2, crossAxisSpacing: StyleString.cardSpace / 2, diff --git a/lib/pages/episode_panel/view.dart b/lib/pages/episode_panel/view.dart index f8e633fcc..1634e1d7f 100644 --- a/lib/pages/episode_panel/view.dart +++ b/lib/pages/episode_panel/view.dart @@ -24,6 +24,7 @@ import 'package:PiliPlus/pages/common/common_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; diff --git a/lib/pages/fav/pgc/controller.dart b/lib/pages/fav/pgc/controller.dart index 4a0d2c81f..1535fc77b 100644 --- a/lib/pages/fav/pgc/controller.dart +++ b/lib/pages/fav/pgc/controller.dart @@ -4,7 +4,7 @@ import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models_new/fav/fav_pgc/data.dart'; import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart'; import 'package:PiliPlus/pages/common/multi_select_controller.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; diff --git a/lib/pages/follow/controller.dart b/lib/pages/follow/controller.dart index 087b2d82e..8f9b44574 100644 --- a/lib/pages/follow/controller.dart +++ b/lib/pages/follow/controller.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/member.dart'; import 'package:PiliPlus/models/member/tags.dart'; import 'package:PiliPlus/services/account_service.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/history/base_controller.dart b/lib/pages/history/base_controller.dart index 3e6eaffb5..c315f30ce 100644 --- a/lib/pages/history/base_controller.dart +++ b/lib/pages/history/base_controller.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/history/controller.dart b/lib/pages/history/controller.dart index 44ab3e211..83a658cf5 100644 --- a/lib/pages/history/controller.dart +++ b/lib/pages/history/controller.dart @@ -7,6 +7,7 @@ import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/pages/history/base_controller.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 75bcefda4..c44e9f307 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -9,6 +9,8 @@ import 'package:PiliPlus/pages/mine/view.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -19,10 +21,10 @@ class HomeController extends GetxController late TabController tabController; StreamController? searchBarStream; - late bool hideSearchBar; - late bool useSideBar; + final bool hideSearchBar = Pref.hideSearchBar; + final bool useSideBar = Pref.useSideBar; - late bool enableSearchWord; + final bool enableSearchWord = Pref.enableSearchWord; late RxString defaultSearch = ''.obs; late int lateCheckSearchAt = 0; @@ -37,22 +39,15 @@ class HomeController extends GetxController void onInit() { super.onInit(); - hideSearchBar = - GStorage.setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); if (hideSearchBar) { searchBarStream = StreamController.broadcast(); } - enableSearchWord = GStorage.setting - .get(SettingBoxKey.enableSearchWord, defaultValue: true); if (enableSearchWord) { lateCheckSearchAt = DateTime.now().millisecondsSinceEpoch; querySearchDefault(); } - useSideBar = - GStorage.setting.get(SettingBoxKey.useSideBar, defaultValue: false); - setTabConfig(); } diff --git a/lib/pages/hot/controller.dart b/lib/pages/hot/controller.dart index cf39cea8c..8b35af31f 100644 --- a/lib/pages/hot/controller.dart +++ b/lib/pages/hot/controller.dart @@ -2,12 +2,12 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:get/get.dart'; class HotController extends CommonListController, HotVideoItemModel> { - late RxBool showHotRcmd = GStorage.showHotRcmd.obs; + final RxBool showHotRcmd = Pref.showHotRcmd.obs; @override void onInit() { diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 4883207e4..23f2ce7d0 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -5,9 +5,9 @@ import 'package:PiliPlus/models_new/later/list.dart'; import 'package:PiliPlus/pages/history/view.dart' show AppBarWidget; import 'package:PiliPlus/pages/later/base_controller.dart'; import 'package:PiliPlus/pages/later/controller.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index b9946200e..1e4600db4 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -15,9 +15,10 @@ import 'package:PiliPlus/plugin/pl_player/models/data_source.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/tcp/live.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/danmaku_utils.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/video_utils.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; @@ -86,8 +87,8 @@ class LiveRoomController extends GetxController { if (currentQn == null) { await Connectivity().checkConnectivity().then((res) { currentQn = res.contains(ConnectivityResult.wifi) - ? GStorage.liveQuality - : GStorage.liveQualityCellular; + ? Pref.liveQuality + : Pref.liveQualityCellular; }); } var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn); diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index c25d53f9f..217057eb6 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -17,6 +17,7 @@ import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; @@ -109,8 +110,8 @@ class _LiveRoomPageState extends State double _getFontSize(bool isFullScreen) { return isFullScreen == false || _isPipMode == true - ? 15 * plPlayerController.fontSize - : 15 * plPlayerController.fontSizeFS; + ? 15 * plPlayerController.danmakuFontScale + : 15 * plPlayerController.danmakuFontScaleFS; } void videoSourceInit() { @@ -180,7 +181,7 @@ class _LiveRoomPageState extends State ), danmuWidget: Obx( () => AnimatedOpacity( - opacity: plPlayerController.isOpenDanmu.value ? 1 : 0, + opacity: plPlayerController.enableShowDanmaku.value ? 1 : 0, duration: const Duration(milliseconds: 100), child: DanmakuScreen( createdController: (DanmakuController e) { @@ -191,7 +192,7 @@ class _LiveRoomPageState extends State fontSize: _getFontSize(isFullScreen), fontWeight: plPlayerController.fontWeight, area: plPlayerController.showArea, - opacity: plPlayerController.opacity, + opacity: plPlayerController.danmakuOpacity, hideTop: plPlayerController.blockTypes.contains(5), hideScroll: plPlayerController.blockTypes.contains(2), hideBottom: plPlayerController.blockTypes.contains(4), @@ -564,13 +565,13 @@ class _LiveRoomPageState extends State Obx( () => IconButton( onPressed: () { - plPlayerController.isOpenDanmu.value = - !plPlayerController.isOpenDanmu.value; + plPlayerController.enableShowDanmaku.value = + !plPlayerController.enableShowDanmaku.value; GStorage.setting.put(SettingBoxKey.enableShowDanmaku, - plPlayerController.isOpenDanmu.value); + plPlayerController.enableShowDanmaku.value); }, icon: Icon( - plPlayerController.isOpenDanmu.value + plPlayerController.enableShowDanmaku.value ? Icons.subtitles_outlined : Icons.subtitles_off_outlined, color: _color, diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 9c90fa494..3a07ebbd5 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -3,6 +3,7 @@ import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -85,14 +86,14 @@ class BottomControl extends StatelessWidget { padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { - plPlayerController.isOpenDanmu.value = - !plPlayerController.isOpenDanmu.value; + plPlayerController.enableShowDanmaku.value = + !plPlayerController.enableShowDanmaku.value; GStorage.setting.put(SettingBoxKey.enableShowDanmaku, - plPlayerController.isOpenDanmu.value); + plPlayerController.enableShowDanmaku.value); }, icon: Icon( size: 18, - plPlayerController.isOpenDanmu.value + plPlayerController.enableShowDanmaku.value ? Icons.subtitles_outlined : Icons.subtitles_off_outlined, color: Colors.white, diff --git a/lib/pages/live_search/child/view.dart b/lib/pages/live_search/child/view.dart index 290c36a27..265dd76fc 100644 --- a/lib/pages/live_search/child/view.dart +++ b/lib/pages/live_search/child/view.dart @@ -8,7 +8,6 @@ import 'package:PiliPlus/pages/live_search/child/controller.dart'; import 'package:PiliPlus/pages/live_search/widgets/live_search_room.dart'; import 'package:PiliPlus/pages/live_search/widgets/live_search_user.dart'; import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -28,8 +27,6 @@ class LiveSearchChildPage extends StatefulWidget { class _LiveSearchChildPageState extends State with AutomaticKeepAliveClientMixin { - late final bool dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); LiveSearchChildController get _controller => widget.controller; @override diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 2c229de1a..31076835d 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -8,8 +8,8 @@ import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/login.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/login/model.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 07c843161..39441255c 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -10,6 +10,8 @@ import 'package:PiliPlus/models_new/msgfeed_unread/data.dart'; import 'package:PiliPlus/models_new/single_unread/data.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/update.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -22,29 +24,32 @@ class MainController extends GetxController RxInt dynCount = 0.obs; StreamController? bottomBarStream; - late bool hideTabBar; + late bool hideTabBar = Pref.hideTabBar; late dynamic controller; RxInt selectedIndex = 0.obs; late DynamicBadgeMode dynamicBadgeMode; - late bool checkDynamic = GStorage.checkDynamic; - late int dynamicPeriod = GStorage.dynamicPeriod; + late bool checkDynamic = Pref.checkDynamic; + late int dynamicPeriod = Pref.dynamicPeriod; late int _lastCheckDynamicAt = 0; late int dynIndex = -1; late int homeIndex = -1; - late DynamicBadgeMode msgBadgeMode = GStorage.msgBadgeMode; - late Set msgUnReadTypes = GStorage.msgUnReadTypeV2; + late DynamicBadgeMode msgBadgeMode = Pref.msgBadgeMode; + late Set msgUnReadTypes = Pref.msgUnReadTypeV2; late final RxString msgUnReadCount = ''.obs; late int lastCheckUnreadAt = 0; - late final mainTabBarView = GStorage.mainTabBarView; - late bool navSearchStreamDebounce = GStorage.navSearchStreamDebounce; + final enableMYBar = Pref.enableMYBar; + final useSideBar = Pref.useSideBar; + final mainTabBarView = Pref.mainTabBarView; + late bool navSearchStreamDebounce = Pref.navSearchStreamDebounce; + late final optTabletNav = Pref.optTabletNav; @override void onInit() { super.onInit(); - if (GStorage.autoUpdate) { + if (Pref.autoUpdate) { Update.checkUpdate(); } @@ -58,14 +63,10 @@ class MainController extends GetxController ) : PageController(initialPage: selectedIndex.value); - hideTabBar = - GStorage.setting.get(SettingBoxKey.hideTabBar, defaultValue: true); if (navigationBars.length > 1 && hideTabBar) { bottomBarStream = StreamController.broadcast(); } - dynamicBadgeMode = DynamicBadgeMode.values[GStorage.setting.get( - SettingBoxKey.dynamicBadgeMode, - defaultValue: DynamicBadgeMode.number.index)]; + dynamicBadgeMode = DynamicBadgeMode.values[Pref.dynamicBadgeMode]; dynIndex = navigationBars.indexOf(NavigationBarType.dynamics); if (dynamicBadgeMode != DynamicBadgeMode.hidden) { @@ -193,7 +194,7 @@ class MainController extends GetxController void setNavBarConfig() { List? navBarSort = (GStorage.setting.get(SettingBoxKey.navBarSort) as List?)?.cast(); - int defaultHomePage = GStorage.defaultHomePage; + int defaultHomePage = Pref.defaultHomePage; late final List navigationBars; if (navBarSort == null) { navigationBars = NavigationBarType.values; diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 429fdcc8a..0fa74b2a6 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -40,17 +40,10 @@ class _MainAppState extends State late final _dynamicController = Get.put(DynamicsController()); late int _lastSelectTime = 0; - late bool enableMYBar; - late bool useSideBar; @override void initState() { super.initState(); - _lastSelectTime = DateTime.now().millisecondsSinceEpoch; - enableMYBar = - GStorage.setting.get(SettingBoxKey.enableMYBar, defaultValue: true); - useSideBar = - GStorage.setting.get(SettingBoxKey.useSideBar, defaultValue: false); WidgetsBinding.instance.addObserver(this); } @@ -198,9 +191,9 @@ class _MainAppState extends State child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (useSideBar || !isPortrait) ...[ + if (_mainController.useSideBar || !isPortrait) ...[ _mainController.navigationBars.length > 1 - ? context.isTablet && GStorage.optTabletNav + ? context.isTablet && _mainController.optTabletNav ? Column( children: [ const SizedBox(height: 25), @@ -295,7 +288,7 @@ class _MainAppState extends State ], ), ), - bottomNavigationBar: useSideBar || !isPortrait + bottomNavigationBar: _mainController.useSideBar || !isPortrait ? null : StreamBuilder( stream: _mainController.hideTabBar @@ -311,7 +304,7 @@ class _MainAppState extends State curve: Curves.easeInOutCubicEmphasized, duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), - child: enableMYBar + child: _mainController.enableMYBar ? _mainController.navigationBars.length > 1 ? Obx( () => NavigationBar( diff --git a/lib/pages/match_info/controller.dart b/lib/pages/match_info/controller.dart index 2b4848a5a..2f35a4ff1 100644 --- a/lib/pages/match_info/controller.dart +++ b/lib/pages/match_info/controller.dart @@ -43,6 +43,5 @@ class MatchInfoController extends ReplyController { mode: mode.value, cursorNext: cursorNext, offset: paginationReply?.nextOffset, - antiGoodsReply: antiGoodsReply, ); } diff --git a/lib/pages/match_info/view.dart b/lib/pages/match_info/view.dart index 7497bd937..71b1dfc08 100644 --- a/lib/pages/match_info/view.dart +++ b/lib/pages/match_info/view.dart @@ -7,7 +7,6 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/image_type.dart'; -import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; import 'package:PiliPlus/models_new/match/match_info/contest.dart'; import 'package:PiliPlus/models_new/match/match_info/team.dart'; import 'package:PiliPlus/pages/match_info/controller.dart'; diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 3e4e8f755..259f12d2f 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -13,7 +13,7 @@ import 'package:PiliPlus/pages/common/common_data_controller.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart' show ExtendedNestedScrollViewState; @@ -102,8 +102,8 @@ class MemberController extends CommonDataController } if (tab2!.isNotEmpty) { int initialIndex = -1; - MemberTabType memberTab = GStorage.memberTab; - if (memberTab != MemberTabType.none) { + MemberTabType memberTab = Pref.memberTab; + if (memberTab != MemberTabType.def) { initialIndex = tab2!.indexWhere((item) { return item.param == memberTab.name; }); diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index 0b9b34657..7c1a78f61 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -4,11 +4,11 @@ import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models_new/space/space/card.dart'; import 'package:PiliPlus/models_new/space/space/images.dart'; import 'package:PiliPlus/models_new/space/space/live.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index fdf3dc026..2829bf090 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -6,8 +6,8 @@ import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/member_dynamics/controller.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -26,7 +26,6 @@ class _MemberDynamicsPageState extends State with AutomaticKeepAliveClientMixin { late MemberDynamicsController _memberDynamicController; late int mid; - late bool dynamicsWaterfallFlow; @override bool get wantKeepAlive => true; @@ -38,8 +37,6 @@ class _MemberDynamicsPageState extends State final String heroTag = Utils.makeHeroTag(mid); _memberDynamicController = Get.put(MemberDynamicsController(mid), tag: heroTag); - dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); } @override @@ -71,9 +68,10 @@ class _MemberDynamicsPageState extends State Widget _buildContent(LoadingState?> loadingState) { return switch (loadingState) { - Loading() => DynamicsTabPage.dynSkeleton(dynamicsWaterfallFlow), + Loading() => + DynamicsTabPage.dynSkeleton(GlobalData().dynamicsWaterfallFlow), Success(:var response) => response?.isNotEmpty == true - ? dynamicsWaterfallFlow + ? GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow.extent( maxCrossAxisExtent: Grid.smallCardWidth * 2, crossAxisSpacing: StyleString.safeSpace, diff --git a/lib/pages/member_profile/view.dart b/lib/pages/member_profile/view.dart index bf2e625a1..4e67ff922 100644 --- a/lib/pages/member_profile/view.dart +++ b/lib/pages/member_profile/view.dart @@ -8,6 +8,7 @@ import 'package:PiliPlus/models/user/info.dart'; import 'package:PiliPlus/models_new/account_myinfo/data.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/services/account_service.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; diff --git a/lib/pages/member_search/child/view.dart b/lib/pages/member_search/child/view.dart index 27ae848f0..2d647e9e8 100644 --- a/lib/pages/member_search/child/view.dart +++ b/lib/pages/member_search/child/view.dart @@ -8,8 +8,8 @@ import 'package:PiliPlus/models/common/member/search_type.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/member_search/child/controller.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:waterfall_flow/waterfall_flow.dart'; @@ -30,8 +30,6 @@ class MemberSearchChildPage extends StatefulWidget { class _MemberSearchChildPageState extends State with AutomaticKeepAliveClientMixin { - late final bool dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); MemberSearchChildController get _controller => widget.controller; @override @@ -67,7 +65,7 @@ class _MemberSearchChildPageState extends State ), ), MemberSearchType.dynamic => - DynamicsTabPage.dynSkeleton(dynamicsWaterfallFlow), + DynamicsTabPage.dynSkeleton(GlobalData().dynamicsWaterfallFlow), }; } @@ -92,7 +90,7 @@ class _MemberSearchChildPageState extends State childCount: response!.length, ), ), - MemberSearchType.dynamic => dynamicsWaterfallFlow + MemberSearchType.dynamic => GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow.extent( maxCrossAxisExtent: Grid.smallCardWidth * 2, crossAxisSpacing: StyleString.safeSpace, diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 156bd2d19..224265661 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -4,13 +4,14 @@ import 'package:PiliPlus/models/common/theme/theme_type.dart'; import 'package:PiliPlus/models/user/info.dart'; import 'package:PiliPlus/models/user/stat.dart'; import 'package:PiliPlus/services/account_service.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/login_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; class MineController extends GetxController { @@ -22,7 +23,6 @@ class MineController extends GetxController { AccountService accountService = Get.find(); Rx themeType = ThemeType.system.obs; - Box get setting => GStorage.setting; static RxBool anonymity = (Accounts.account.isNotEmpty && !Accounts.get(AccountType.heartbeat).isLogin) .obs; @@ -200,7 +200,7 @@ class MineController extends GetxController { try { Get.find().themeType.value = themeType.value; } catch (_) {} - setting.put(SettingBoxKey.themeMode, themeType.value.code); + GStorage.setting.put(SettingBoxKey.themeMode, themeType.value.index); Get.changeThemeMode(themeType.value.toThemeMode); } diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 053081c02..9469cab50 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -1,7 +1,6 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/image_type.dart'; -import 'package:PiliPlus/models/common/theme/theme_type.dart'; import 'package:PiliPlus/models/user/info.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/utils/extension.dart'; @@ -57,7 +56,7 @@ class _MinePageState extends State { style: const ButtonStyle( tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - tooltip: '切换至${_mineController.nextThemeType.description}主题', + tooltip: '切换至${_mineController.nextThemeType.desc}主题', onPressed: _mineController.onChangeTheme, icon: _mineController.themeType.value.icon, ); diff --git a/lib/pages/pgc/controller.dart b/lib/pages/pgc/controller.dart index 3e7d66ca0..d06b4719c 100644 --- a/lib/pages/pgc/controller.dart +++ b/lib/pages/pgc/controller.dart @@ -9,7 +9,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_timeline/result.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -19,7 +19,7 @@ class PgcController final HomeTabType tabType; late final showPgcTimeline = - tabType == HomeTabType.bangumi && GStorage.showPgcTimeline; + tabType == HomeTabType.bangumi && Pref.showPgcTimeline; AccountService accountService = Get.find(); diff --git a/lib/pages/pgc_review/child/view.dart b/lib/pages/pgc_review/child/view.dart index a7fe2b190..bb5f16ddd 100644 --- a/lib/pages/pgc_review/child/view.dart +++ b/lib/pages/pgc_review/child/view.dart @@ -11,9 +11,9 @@ import 'package:PiliPlus/models/common/pgc_review_type.dart'; import 'package:PiliPlus/models_new/pgc/pgc_review/list.dart'; import 'package:PiliPlus/pages/pgc_review/child/controller.dart'; import 'package:PiliPlus/pages/pgc_review/post/view.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/num_util.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/pgc_review/post/view.dart b/lib/pages/pgc_review/post/view.dart index a6f2538e0..0737fd4e6 100644 --- a/lib/pages/pgc_review/post/view.dart +++ b/lib/pages/pgc_review/post/view.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/custom_icon.dart'; import 'package:PiliPlus/http/pgc.dart'; import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 874f86373..54c777cc2 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -1,15 +1,14 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; class RcmdController extends CommonListController { - late bool enableSaveLastData = GStorage.setting - .get(SettingBoxKey.enableSaveLastData, defaultValue: false); - final bool appRcmd = GStorage.appRcmd; + late bool enableSaveLastData = Pref.enableSaveLastData; + final bool appRcmd = Pref.appRcmd; int? lastRefreshAt; - late bool savedRcmdTip = GStorage.savedRcmdTip; + late bool savedRcmdTip = Pref.savedRcmdTip; @override void onInit() { diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart index 25098a6a0..e4c2dc9ae 100644 --- a/lib/pages/search/controller.dart +++ b/lib/pages/search/controller.dart @@ -7,6 +7,7 @@ import 'package:PiliPlus/models/search/suggest.dart'; import 'package:PiliPlus/models_new/search/search_rcmd/data.dart'; import 'package:PiliPlus/models_new/search/search_trending/data.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:stream_transform/stream_transform.dart'; @@ -27,21 +28,21 @@ class SSearchController extends GetxController { late final digitOnlyRegExp = RegExp(r'^\d+$'); // history - late final RxBool recordSearchHistory; + late final RxBool recordSearchHistory = Pref.recordSearchHistory.obs; late final RxList historyList; // suggestion - late final bool searchSuggestion; + late final bool searchSuggestion = Pref.searchSuggestion; StreamController? _ctr; StreamSubscription? _sub; late final RxList searchSuggestList; // trending - late final bool enableHotKey; + late final bool enableHotKey = Pref.enableHotKey; late final Rx> loadingState; // rcmd - late final bool enableSearchRcmd; + late final bool enableSearchRcmd = Pref.enableSearchRcmd; late final Rx> recommendData; @override @@ -58,13 +59,6 @@ class SSearchController extends GetxController { controller.text = Get.parameters['text']!; } - searchSuggestion = GStorage.searchSuggestion; - enableHotKey = - GStorage.setting.get(SettingBoxKey.enableHotKey, defaultValue: true); - enableSearchRcmd = GStorage.setting - .get(SettingBoxKey.enableSearchRcmd, defaultValue: true); - recordSearchHistory = GStorage.recordSearchHistory.obs; - if (recordSearchHistory.value) { historyList = List.from(GStorage.historyWord.get('cacheList') ?? []).obs; diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index d52d71989..920d69e9d 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -7,6 +7,7 @@ import 'package:PiliPlus/pages/search/widgets/hot_keyword.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/utils/em.dart' show Em; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/search_panel/video/controller.dart b/lib/pages/search_panel/video/controller.dart index 1d1ba77b1..75a73f3e2 100644 --- a/lib/pages/search_panel/video/controller.dart +++ b/lib/pages/search_panel/video/controller.dart @@ -88,7 +88,7 @@ class SearchVideoController // sort late final List filterList = ArchiveFilterType.values .map((type) => { - 'label': type.description, + 'label': type.desc, 'type': type, }) .toList(); diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 3161779ed..358979ca5 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -1,20 +1,29 @@ -import 'package:PiliPlus/pages/setting/widgets/model.dart'; +import 'package:PiliPlus/pages/setting/models/extra_settings.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; import 'package:flutter/material.dart'; -class ExtraSetting extends StatelessWidget { +class ExtraSetting extends StatefulWidget { const ExtraSetting({super.key, this.showAppBar}); final bool? showAppBar; + @override + State createState() => _ExtraSettingState(); +} + +class _ExtraSettingState extends State { + final settings = extraSettings; + @override Widget build(BuildContext context) { return Scaffold( - appBar: showAppBar == false ? null : AppBar(title: const Text('其它设置')), + appBar: + widget.showAppBar == false ? null : AppBar(title: const Text('其它设置')), body: ListView( - children: [ - ...extraSettings.map((item) => item.widget), - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), - ], + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + children: settings.map((item) => item.widget).toList(), ), ); } diff --git a/lib/pages/setting/models/extra_settings.dart b/lib/pages/setting/models/extra_settings.dart new file mode 100644 index 000000000..6872edce9 --- /dev/null +++ b/lib/pages/setting/models/extra_settings.dart @@ -0,0 +1,1099 @@ +import 'dart:io'; +import 'dart:math' show pi, max; + +import 'package:PiliPlus/common/widgets/image/image_view.dart'; +import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; +import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/grpc/reply.dart'; +import 'package:PiliPlus/models/common/audio_normalization.dart'; +import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; +import 'package:PiliPlus/models/common/member/tab_type.dart'; +import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/models/common/super_resolution_type.dart'; +import 'package:PiliPlus/models/dynamics/result.dart'; +import 'package:PiliPlus/pages/common/common_slide_page.dart'; +import 'package:PiliPlus/pages/home/controller.dart'; +import 'package:PiliPlus/pages/hot/controller.dart'; +import 'package:PiliPlus/pages/main/controller.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart'; +import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; +import 'package:PiliPlus/utils/cache_manage.dart'; +import 'package:PiliPlus/utils/feed_back.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:PiliPlus/utils/update.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; +import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; + +List get extraSettings => [ + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '空降助手', + subtitle: '点击配置', + setKey: SettingBoxKey.enableSponsorBlock, + defaultVal: false, + onTap: () => Get.toNamed('/sponsorBlock'), + leading: const Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Icon(Icons.shield_outlined), + Icon(Icons.play_arrow_rounded, size: 15), + ], + ), + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '检查未读动态', + subtitle: '点击设置检查周期(min)', + leading: const Icon(Icons.notifications_none), + setKey: SettingBoxKey.checkDynamic, + defaultVal: true, + onChanged: (value) { + Get.find().checkDynamic = value; + }, + onTap: () { + int dynamicPeriod = Pref.dynamicPeriod; + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('检查周期'), + content: TextFormField( + autofocus: true, + initialValue: dynamicPeriod.toString(), + keyboardType: TextInputType.number, + onChanged: (value) { + dynamicPeriod = int.tryParse(value) ?? 5; + }, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'\d+')), + ], + decoration: const InputDecoration(suffixText: 'min'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + GStorage.setting + .put(SettingBoxKey.dynamicPeriod, dynamicPeriod); + Get.find().dynamicPeriod = dynamicPeriod; + }, + child: const Text('确定'), + ) + ], + ); + }, + ); + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示视频分段信息', + leading: Transform.rotate( + angle: pi / 2, + child: const Icon(MdiIcons.viewHeadline), + ), + setKey: SettingBoxKey.showViewPoints, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '视频页显示相关视频', + leading: const Icon(MdiIcons.motionPlayOutline), + setKey: SettingBoxKey.showRelatedVideo, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示视频评论', + leading: const Icon(MdiIcons.commentTextOutline), + setKey: SettingBoxKey.showVideoReply, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示番剧评论', + leading: const Icon(MdiIcons.commentTextOutline), + setKey: SettingBoxKey.showBangumiReply, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '默认展开视频简介', + leading: const Icon(Icons.expand_more), + setKey: SettingBoxKey.alwaysExapndIntroPanel, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '横屏自动展开视频简介', + leading: const Icon(Icons.expand_more), + setKey: SettingBoxKey.exapndIntroPanelH, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '横屏分P/合集列表显示在Tab栏', + leading: const Icon(Icons.format_list_numbered_rtl_sharp), + setKey: SettingBoxKey.horizontalSeasonPanel, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '横屏播放页在侧栏打开UP主页', + leading: const Icon(Icons.account_circle_outlined), + setKey: SettingBoxKey.horizontalMemberPage, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '评论折叠行数', + subtitle: '0行为不折叠', + leading: const Icon(Icons.compress), + setKey: SettingBoxKey.replyLengthLimit, + getTrailing: () => Text( + '${ReplyItemGrpc.replyLengthLimit}行', + style: Get.theme.textTheme.titleSmall, + ), + onTap: (setState) { + String replyLengthLimit = ReplyItemGrpc.replyLengthLimit.toString(); + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('评论折叠行数'), + content: TextFormField( + autofocus: true, + initialValue: replyLengthLimit, + keyboardType: TextInputType.number, + onChanged: (value) { + replyLengthLimit = value; + }, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'\d+')), + ], + decoration: const InputDecoration(suffixText: '行'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + ReplyItemGrpc.replyLengthLimit = + int.tryParse(replyLengthLimit) ?? 6; + await GStorage.setting.put( + SettingBoxKey.replyLengthLimit, + ReplyItemGrpc.replyLengthLimit, + ); + setState(); + }, + child: const Text('确定'), + ) + ], + ); + }, + ); + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '弹幕行高', + subtitle: '默认1.6', + setKey: SettingBoxKey.danmakuLineHeight, + leading: const Icon(Icons.subtitles_outlined), + getTrailing: () => Text( + Pref.danmakuLineHeight.toString(), + style: Get.theme.textTheme.titleSmall, + ), + onTap: (setState) { + String danmakuLineHeight = Pref.danmakuLineHeight.toString(); + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('弹幕行高'), + content: TextFormField( + autofocus: true, + initialValue: danmakuLineHeight, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + onChanged: (value) { + danmakuLineHeight = value; + }, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + await GStorage.setting.put( + SettingBoxKey.danmakuLineHeight, + max( + 1.0, + double.tryParse(danmakuLineHeight)?.toPrecision(1) ?? + 1.6, + ), + ); + setState(); + }, + child: const Text('确定'), + ) + ], + ); + }, + ); + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示视频警告/争议信息', + leading: const Icon(Icons.warning_amber_rounded), + setKey: SettingBoxKey.showArgueMsg, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '分P/合集:倒序播放从首集开始播放', + subtitle: '开启则自动切换为倒序首集,否则保持当前集', + leading: const Icon(MdiIcons.sort), + setKey: SettingBoxKey.reverseFromFirst, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '禁用 SSL 证书验证', + subtitle: '谨慎开启,禁用容易受到中间人攻击', + leading: const Icon(Icons.security), + needReboot: true, + setKey: SettingBoxKey.badCertificateCallback, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示继续播放分P提示', + leading: const Icon(Icons.local_parking), + setKey: SettingBoxKey.continuePlayingPart, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '横屏在侧栏打开图片预览', + leading: const Icon(Icons.photo_outlined), + setKey: SettingBoxKey.horizontalPreview, + defaultVal: false, + ), + getBanwordModel( + context: Get.context!, + title: '评论关键词过滤', + key: SettingBoxKey.banWordForReply, + callback: (value) { + ReplyGrpc.replyRegExp = value; + ReplyGrpc.enableFilter = value.pattern.isNotEmpty; + }, + ), + getBanwordModel( + context: Get.context!, + title: '动态关键词过滤', + key: SettingBoxKey.banWordForDyn, + callback: (value) { + DynamicsDataModel.banWordForDyn = value; + DynamicsDataModel.enableFilter = value.pattern.isNotEmpty; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '使用外部浏览器打开链接', + leading: const Icon(Icons.open_in_browser), + setKey: SettingBoxKey.openInBrowser, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '刷新滑动距离', + leading: const Icon(Icons.refresh), + setKey: SettingBoxKey.refreshDragPercentage, + getSubtitle: () => '当前滑动距离: ${Pref.refreshDragPercentage}x', + onTap: (setState) async { + double? result = await showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: '刷新滑动距离', + min: 0.1, + max: 0.5, + divisions: 8, + precise: 2, + value: Pref.refreshDragPercentage, + suffix: 'x', + ); + }, + ); + if (result != null) { + kDragContainerExtentPercentage = result; + await GStorage.setting + .put(SettingBoxKey.refreshDragPercentage, result); + Get.forceAppUpdate(); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '刷新指示器高度', + leading: const Icon(Icons.height), + setKey: SettingBoxKey.refreshDisplacement, + getSubtitle: () => '当前指示器高度: ${Pref.refreshDisplacement}', + onTap: (setState) async { + double? result = await showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: '刷新指示器高度', + min: 10.0, + max: 100.0, + divisions: 9, + value: Pref.refreshDisplacement, + ); + }, + ); + if (result != null) { + displacement = result; + await GStorage.setting + .put(SettingBoxKey.refreshDisplacement, result); + Get.forceAppUpdate(); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示会员彩色弹幕', + leading: const Icon(MdiIcons.gradientHorizontal), + setKey: SettingBoxKey.showVipDanmaku, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示高级弹幕', + leading: const Icon(MdiIcons.paletteAdvanced), + setKey: SettingBoxKey.showSpecialDanmaku, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '合并弹幕', + subtitle: '合并一段时间内获取到的相同弹幕', + leading: const Icon(Icons.merge), + setKey: SettingBoxKey.mergeDanmaku, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示热门推荐', + subtitle: '热门页面显示每周必看等推荐内容入口', + leading: const Icon(Icons.local_fire_department_outlined), + setKey: SettingBoxKey.showHotRcmd, + defaultVal: false, + onChanged: (value) { + try { + Get.find().showHotRcmd.value = value; + } catch (_) {} + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '音量均衡', + setKey: SettingBoxKey.audioNormalization, + leading: const Icon(Icons.multitrack_audio), + getSubtitle: () { + String audioNormalization = Pref.audioNormalization; + audioNormalization = switch (audioNormalization) { + '0' => AudioNormalization.disable.title, + '1' => AudioNormalization.dynaudnorm.title, + '2' => AudioNormalization.loudnorm.title, + _ => audioNormalization, + }; + return '当前:「$audioNormalization」'; + }, + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + String audioNormalization = Pref.audioNormalization; + Set values = {'0', '1', '2', audioNormalization, '3'}; + return SelectDialog( + title: '音量均衡', + value: audioNormalization, + values: values + .map((e) => ( + e, + switch (e) { + '0' => AudioNormalization.disable.title, + '1' => AudioNormalization.dynaudnorm.title, + '2' => AudioNormalization.loudnorm.title, + '3' => AudioNormalization.custom.title, + _ => e, + } + )) + .toList()); + }, + ); + if (result != null) { + if (result == '3') { + String param = ''; + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('自定义参数'), + content: TextField( + autofocus: true, + onChanged: (value) => param = value, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + await GStorage.setting + .put(SettingBoxKey.audioNormalization, param); + setState(); + }, + child: const Text('确定'), + ), + ], + ); + }); + } else { + await GStorage.setting + .put(SettingBoxKey.audioNormalization, result); + setState(); + } + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '超分辨率', + leading: const Icon(Icons.stay_current_landscape_outlined), + getSubtitle: () => + '当前:「${SuperResolutionType.values[Pref.superResolutionType].title}」\n默认设置对番剧生效, 其他视频默认关闭\n超分辨率需要启用硬件解码, 若启用硬件解码后仍然不生效, 尝试切换硬件解码器为 auto-copy', + onTap: (setState) async { + SuperResolutionType? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '超分辨率', + value: SuperResolutionType.values[Pref.superResolutionType], + values: SuperResolutionType.values.map((e) { + return (e, e.title); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.superResolutionType, result.index); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '提前初始化播放器', + subtitle: '相对减少手动播放加载时间', + leading: const Icon(Icons.play_circle_outlined), + setKey: SettingBoxKey.preInitPlayer, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '首页切换页面动画', + leading: const Icon(Icons.home_outlined), + setKey: SettingBoxKey.mainTabBarView, + defaultVal: false, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '搜索建议', + leading: const Icon(Icons.search), + setKey: SettingBoxKey.searchSuggestion, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '记录搜索历史', + leading: const Icon(Icons.history), + setKey: SettingBoxKey.recordSearchHistory, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '展示头像/评论/动态装饰', + leading: const Icon(MdiIcons.stickerCircleOutline), + setKey: SettingBoxKey.showDynDecorate, + defaultVal: true, + onChanged: (value) => PendantAvatar.showDynDecorate = value, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '预览 Live Photo', + subtitle: '开启则以视频形式预览 Live Photo,否则预览静态图片', + leading: const Icon(Icons.image_outlined), + setKey: SettingBoxKey.enableLivePhoto, + defaultVal: true, + onChanged: (value) { + ImageModel.enableLivePhoto = value; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '滑动跳转预览视频缩略图', + leading: const Icon(Icons.preview_outlined), + setKey: SettingBoxKey.showSeekPreview, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示高能进度条', + subtitle: '高能进度条反应了在时域上,单位时间内弹幕发送量的变化趋势', + leading: const Icon(Icons.show_chart), + setKey: SettingBoxKey.showDmChart, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '发评反诈', + subtitle: '发送评论后检查评论是否可见', + leading: const Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Icon(Icons.shield_outlined), + Icon(Icons.reply, size: 14), + ], + ), + setKey: SettingBoxKey.enableCommAntifraud, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '使用「哔哩发评反诈」检查评论', + subtitle: '仅对Android生效', + leading: const Icon( + FontAwesomeIcons.b, + size: 22, + ), + setKey: SettingBoxKey.biliSendCommAntifraud, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '发布/转发动态反诈', + subtitle: '发布/转发动态后检查动态是否可见', + leading: const Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Icon(Icons.shield_outlined), + Icon(Icons.motion_photos_on, size: 12), + ], + ), + setKey: SettingBoxKey.enableCreateDynAntifraud, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '屏蔽带货动态', + leading: const Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Icon(Icons.shopping_bag_outlined, size: 14), + Icon(Icons.not_interested), + ], + ), + setKey: SettingBoxKey.antiGoodsDyn, + defaultVal: false, + onChanged: (value) { + DynamicsDataModel.antiGoodsDyn = value; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '屏蔽带货评论', + leading: const Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Icon(Icons.shopping_bag_outlined, size: 14), + Icon(Icons.not_interested), + ], + ), + setKey: SettingBoxKey.antiGoodsReply, + defaultVal: false, + onChanged: (value) { + ReplyGrpc.antiGoodsReply = value; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '侧滑关闭二级评论页面', + leading: Transform.rotate( + angle: pi * 1.5, + child: const Icon(Icons.touch_app), + ), + setKey: SettingBoxKey.slideDismissReplyPage, + defaultVal: Platform.isIOS, + onChanged: (value) { + CommonSlidePageState.slideDismissReplyPage = value; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '全屏展示点赞/投币/收藏等操作按钮', + leading: const Icon(MdiIcons.dotsHorizontalCircleOutline), + setKey: SettingBoxKey.showFSActionItem, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '启用双指缩小视频', + leading: const Icon(Icons.pinch), + setKey: SettingBoxKey.enableShrinkVideoSize, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '动态/专栏详情页展示底部操作栏', + leading: const Icon(Icons.more_horiz), + setKey: SettingBoxKey.showDynActionBar, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '启用拖拽字幕调整底部边距', + leading: const Icon(MdiIcons.dragVariant), + setKey: SettingBoxKey.enableDragSubtitle, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '展示追番时间表', + leading: const Icon(MdiIcons.chartTimelineVariantShimmer), + setKey: SettingBoxKey.showPgcTimeline, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + setKey: SettingBoxKey.feedBackEnable, + onChanged: (value) { + enableFeedback = value; + feedBack(); + }, + leading: const Icon(Icons.vibration_outlined), + title: '震动反馈', + subtitle: '请确定手机设置中已开启震动反馈', + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '大家都在搜', + subtitle: '是否展示「大家都在搜」', + leading: const Icon(Icons.data_thresholding_outlined), + setKey: SettingBoxKey.enableHotKey, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '搜索发现', + subtitle: '是否展示「搜索发现」', + leading: const Icon(Icons.search_outlined), + setKey: SettingBoxKey.enableSearchRcmd, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '搜索默认词', + subtitle: '是否展示搜索框默认词', + leading: const Icon(Icons.whatshot_outlined), + setKey: SettingBoxKey.enableSearchWord, + defaultVal: true, + onChanged: (val) { + Get.find().defaultSearch.value = ''; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '快速收藏', + subtitle: '点按收藏至默认,长按选择文件夹', + leading: const Icon(Icons.bookmark_add_outlined), + setKey: SettingBoxKey.enableQuickFav, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '评论区搜索关键词', + subtitle: '展示评论区搜索关键词', + leading: const Icon(Icons.search_outlined), + setKey: SettingBoxKey.enableWordRe, + defaultVal: false, + onChanged: (value) { + ReplyItemGrpc.enableWordRe = value; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '启用AI总结', + subtitle: '视频详情页开启AI总结', + leading: const Icon(Icons.engineering_outlined), + setKey: SettingBoxKey.enableAi, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '消息页禁用"收到的赞"功能', + subtitle: '禁止打开入口,降低网络社交依赖', + leading: const Icon(Icons.beach_access_outlined), + setKey: SettingBoxKey.disableLikeMsg, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '默认展示评论区', + subtitle: '在视频详情页默认切换至评论区页(仅Tab型布局)', + leading: const Icon(Icons.mode_comment_outlined), + setKey: SettingBoxKey.defaultShowComment, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '启用HTTP/2', + leading: const Icon(Icons.swap_horizontal_circle_outlined), + setKey: SettingBoxKey.enableHttp2, + defaultVal: false, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '连接重试次数', + subtitle: '为0时禁用', + leading: const Icon(Icons.repeat), + onTap: (setState) async { + final result = await showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: '连接重试次数', + min: 0, + max: 8, + divisions: 8, + precise: 0, + value: Pref.retryCount.toDouble(), + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.retryCount, result.toInt()); + setState(); + SmartDialog.showToast('重启生效'); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '连接重试间隔', + subtitle: '实际间隔 = 间隔 * 第x次重试', + leading: const Icon(Icons.more_time_outlined), + onTap: (setState) async { + final result = await showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: '连接重试间隔', + min: 0, + max: 1000, + divisions: 10, + precise: 0, + value: Pref.retryDelay.toDouble(), + suffix: 'ms', + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.retryDelay, result.toInt()); + setState(); + SmartDialog.showToast('重启生效'); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '评论展示', + setKey: SettingBoxKey.replySortType, + leading: const Icon(Icons.whatshot_outlined), + getSubtitle: () => + '当前优先展示「${ReplySortType.values[Pref.replySortType].title}」', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '评论展示', + value: Pref.replySortType, + values: ReplySortType.values.map((e) { + return (e.index, e.title); + }).toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.replySortType, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '动态展示', + setKey: SettingBoxKey.defaultDynamicType, + leading: const Icon(Icons.dynamic_feed_outlined), + getSubtitle: () => + '当前优先展示「${DynamicsTabType.values[Pref.defaultDynamicType].label}」', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '动态展示', + value: Pref.defaultDynamicType, + values: DynamicsTabType.values.sublist(0, 4).map((e) { + return (e.index, e.label); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.defaultDynamicType, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '用户页默认展示TAB', + setKey: SettingBoxKey.memberTab, + leading: const Icon(Icons.tab), + getSubtitle: () => '当前优先展示「${Pref.memberTab.title}」', + onTap: (setState) async { + MemberTabType? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '用户页默认展示TAB', + value: Pref.memberTab, + values: MemberTabType.values.map((e) { + return (e, e.title); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.memberTab, result.index); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + onTap: () { + String systemProxyHost = Pref.systemProxyHost; + String systemProxyPort = Pref.systemProxyPort; + + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('设置代理'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 6), + TextField( + decoration: InputDecoration( + isDense: true, + labelText: systemProxyHost != '' + ? systemProxyHost + : '请输入Host,使用 . 分割', + border: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + hintText: systemProxyHost, + ), + onChanged: (e) { + systemProxyHost = e; + }, + ), + const SizedBox(height: 10), + TextField( + keyboardType: TextInputType.number, + decoration: InputDecoration( + isDense: true, + labelText: + systemProxyPort != '' ? systemProxyPort : '请输入Port', + border: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + hintText: systemProxyPort, + ), + onChanged: (e) { + systemProxyPort = e; + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () { + Get.back(); + GStorage.setting + .put(SettingBoxKey.systemProxyHost, systemProxyHost); + GStorage.setting + .put(SettingBoxKey.systemProxyPort, systemProxyPort); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + }, + leading: const Icon(Icons.airplane_ticket_outlined), + title: '设置代理', + subtitle: '设置代理 host:port', + setKey: SettingBoxKey.enableSystemProxy, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '自动清除缓存', + subtitle: '每次启动时清除缓存', + leading: const Icon(Icons.auto_delete_outlined), + setKey: SettingBoxKey.autoClearCache, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '最大缓存大小', + getSubtitle: () { + final num = Pref.maxCacheSize; + return '当前最大缓存大小: 「${num == 0 ? '无限' : CacheManage.formatSize(Pref.maxCacheSize)}」'; + }, + onTap: (setState) { + showDialog( + context: Get.context!, + builder: (context) { + String valueStr = ''; + return AlertDialog( + title: const Text('最大缓存大小'), + content: TextField( + autofocus: true, + onChanged: (value) => valueStr = value, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ], + decoration: const InputDecoration(suffixText: 'MB'), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + num value = num.tryParse(valueStr) ?? 0; + await GStorage.setting + .put(SettingBoxKey.maxCacheSize, value * 1024 * 1024); + setState(); + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + }, + leading: const Icon(Icons.delete_outlined), + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '检查更新', + subtitle: '每次启动时检查是否需要更新', + leading: const Icon(Icons.system_update_alt_outlined), + setKey: SettingBoxKey.autoUpdate, + defaultVal: true, + onChanged: (val) { + if (val) { + Update.checkUpdate(false); + } + }, + ), + ]; diff --git a/lib/pages/setting/models/model.dart b/lib/pages/setting/models/model.dart new file mode 100644 index 000000000..36c5fd5ea --- /dev/null +++ b/lib/pages/setting/models/model.dart @@ -0,0 +1,221 @@ +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/pages/setting/widgets/normal_item.dart'; +import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show FilteringTextInputFormatter; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; + +extension SettingsModelExt on SettingsModel { + Widget get widget => switch (settingsType) { + SettingsType.normal => NormalItem( + title: title, + getTitle: getTitle, + subtitle: subtitle, + getSubtitle: getSubtitle, + setKey: setKey, + defaultVal: defaultVal, + onChanged: onChanged, + needReboot: needReboot, + leading: leading, + getTrailing: getTrailing, + onTap: onTap, + contentPadding: contentPadding, + titleStyle: titleStyle, + ), + SettingsType.sw1tch => SetSwitchItem( + title: title, + subtitle: subtitle, + setKey: setKey, + defaultVal: defaultVal, + onChanged: onChanged, + needReboot: needReboot, + leading: leading, + onTap: onTap, + contentPadding: contentPadding, + titleStyle: titleStyle, + ), + }; +} + +class SettingsModel { + final SettingsType settingsType; + final String? title; + final Function? getTitle; + final String? subtitle; + final Function? getSubtitle; + final String? setKey; + final bool? defaultVal; + final ValueChanged? onChanged; + final bool? needReboot; + final Widget? leading; + final Function? getTrailing; + final Function? onTap; + final EdgeInsetsGeometry? contentPadding; + final TextStyle? titleStyle; + + SettingsModel({ + required this.settingsType, + this.title, + this.getTitle, + this.subtitle, + this.getSubtitle, + this.setKey, + this.defaultVal, + this.onChanged, + this.needReboot, + this.leading, + this.getTrailing, + this.onTap, + this.contentPadding, + this.titleStyle, + }); +} + +SettingsModel getBanwordModel({ + required BuildContext context, + required String title, + required String key, + required ValueChanged callback, +}) { + String banWord = GStorage.setting.get(key, defaultValue: ''); + return SettingsModel( + settingsType: SettingsType.normal, + leading: const Icon(Icons.filter_alt_outlined), + title: title, + getSubtitle: () => banWord.isEmpty ? "点击添加" : banWord, + onTap: (setState) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(title), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('使用|隔开,如:尝试|测试'), + TextFormField( + autofocus: true, + initialValue: banWord, + textInputAction: TextInputAction.newline, + minLines: 1, + maxLines: 4, + onChanged: (value) => banWord = value, + ) + ], + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: + TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + child: const Text('保存'), + onPressed: () async { + Get.back(); + await GStorage.setting.put(key, banWord); + setState(); + callback(RegExp(banWord, caseSensitive: false)); + SmartDialog.showToast('已保存'); + }, + ), + ], + ); + }, + ); + }, + ); +} + +SettingsModel getVideoFilterSelectModel({ + required BuildContext context, + required String title, + String? subtitle, + String? suffix, + required String key, + required List values, + int defaultValue = 0, + bool isFilter = true, + ValueChanged? onChanged, +}) { + assert(!isFilter || onChanged != null); + int value = GStorage.setting.get(key, defaultValue: defaultValue); + return SettingsModel( + settingsType: SettingsType.normal, + title: '$title${isFilter ? '过滤' : ''}', + leading: const Icon(Icons.timelapse_outlined), + subtitle: subtitle, + getSubtitle: subtitle == null + ? () => isFilter + ? '过滤掉$title小于「$value${suffix ?? ""}」的视频' + : '当前$title:「$value${suffix ?? ""}」' + : null, + onTap: (setState) async { + var result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '选择$title${isFilter ? '(0即不过滤)' : ''}', + value: value, + values: (values + ..addIf(!values.contains(value), value) + ..sort()) + .map((e) => (e, suffix == null ? e.toString() : '$e $suffix')) + .toList() + ..add((-1, '自定义'))); + }, + ); + if (result != null) { + if (result == -1 && context.mounted) { + await showDialog( + context: context, + builder: (context) { + String valueStr = ''; + return AlertDialog( + title: Text('自定义$title'), + content: TextField( + autofocus: true, + onChanged: (value) => valueStr = value, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'\d+')), + ], + decoration: InputDecoration(suffixText: suffix), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () { + Get.back(); + result = int.tryParse(valueStr) ?? 0; + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + } + if (result != -1) { + onChanged?.call(result!); + await GStorage.setting.put(key, result); + setState(); + } + } + }, + ); +} diff --git a/lib/pages/setting/models/play_settings.dart b/lib/pages/setting/models/play_settings.dart new file mode 100644 index 000000000..908538d20 --- /dev/null +++ b/lib/pages/setting/models/play_settings.dart @@ -0,0 +1,262 @@ +import 'dart:io'; + +import 'package:PiliPlus/common/widgets/custom_icon.dart'; +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/models/common/video/subtitle_pref_type.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; +import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart'; +import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; +import 'package:PiliPlus/services/service_locator.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:flutter/material.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'; + +List get playSettings => [ + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '弹幕开关', + subtitle: '是否展示弹幕', + leading: const Icon(CustomIcon.dm_settings), + setKey: SettingBoxKey.enableShowDanmaku, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) => Get.toNamed('/playSpeedSet'), + leading: const Icon(Icons.speed_outlined), + title: '倍速设置', + subtitle: '设置视频播放速度', + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '自动播放', + subtitle: '进入详情页自动播放', + leading: const Icon(Icons.motion_photos_auto_outlined), + setKey: SettingBoxKey.autoPlayEnable, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '双击快退/快进', + subtitle: '左侧双击快退/右侧双击快进,关闭则双击均为暂停/播放', + leading: const Icon(Icons.touch_app_outlined), + setKey: SettingBoxKey.enableQuickDouble, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '左右侧滑动调节亮度/音量', + leading: const Icon(MdiIcons.tuneVerticalVariant), + setKey: SettingBoxKey.enableSlideVolumeBrightness, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '中间滑动进入/退出全屏', + leading: const Icon(MdiIcons.panVertical), + setKey: SettingBoxKey.enableSlideFS, + defaultVal: true, + ), + getVideoFilterSelectModel( + context: Get.context!, + title: '双击快进/快退时长', + suffix: 's', + key: SettingBoxKey.fastForBackwardDuration, + values: [5, 10, 15], + defaultValue: 10, + isFilter: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '滑动快进/快退使用相对时长', + leading: const Icon(Icons.swap_horiz_outlined), + setKey: SettingBoxKey.useRelativeSlide, + defaultVal: false, + ), + getVideoFilterSelectModel( + context: Get.context!, + title: '滑动快进/快退时长', + subtitle: '从播放器一端滑到另一端的快进/快退时长', + suffix: Pref.useRelativeSlide ? '%' : 's', + key: SettingBoxKey.sliderDuration, + values: [25, 50, 90, 100], + defaultValue: 90, + isFilter: false, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '自动启用字幕', + leading: const Icon(Icons.closed_caption_outlined), + getSubtitle: () => + '当前选择偏好:${SubtitlePrefTypeExt.fromCode(Pref.subtitlePreference)!.description}', + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '字幕选择偏好', + value: Pref.subtitlePreference, + values: SubtitlePrefType.values + .map((e) => (e.code, e.description)) + .toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.subtitlePreference, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '竖屏扩大展示', + subtitle: '小屏竖屏视频宽高比由16:9扩大至1:1(不支持收起);横屏适配时,扩大至9:16', + leading: const Icon(Icons.expand_outlined), + setKey: SettingBoxKey.enableVerticalExpand, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '自动全屏', + subtitle: '视频开始播放时进入全屏', + leading: const Icon(Icons.fullscreen_outlined), + setKey: SettingBoxKey.enableAutoEnter, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '自动退出全屏', + subtitle: '视频结束播放时退出全屏', + leading: const Icon(Icons.fullscreen_exit_outlined), + setKey: SettingBoxKey.enableAutoExit, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '延长播放控件显示时间', + subtitle: '开启后延长至30秒,便于屏幕阅读器滑动切换控件焦点', + leading: const Icon(Icons.timer_outlined), + setKey: SettingBoxKey.enableLongShowControl, + defaultVal: false), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '全向旋转', + subtitle: '小屏可受重力转为临时全屏,若系统锁定旋转仍触发请关闭,关闭会影响横屏适配', + leading: const Icon(Icons.screen_rotation_alt_outlined), + setKey: SettingBoxKey.allowRotateScreen, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '后台播放', + subtitle: '进入后台时继续播放', + leading: const Icon(Icons.motion_photos_pause_outlined), + setKey: SettingBoxKey.continuePlayInBackground, + defaultVal: false, + ), + if (Platform.isAndroid) + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '后台画中画', + subtitle: '进入后台时以小窗形式(PiP)播放', + leading: const Icon(Icons.picture_in_picture_outlined), + setKey: SettingBoxKey.autoPiP, + defaultVal: false, + onChanged: (val) { + if (val && !videoPlayerServiceHandler.enableBackgroundPlay) { + SmartDialog.showToast('建议开启后台音频服务'); + } + }), + if (Platform.isAndroid) + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '画中画不加载弹幕', + subtitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验', + leading: const Icon(Icons.subtitles_off_outlined), + setKey: SettingBoxKey.pipNoDanmaku, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '全屏手势反向', + subtitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出', + leading: const Icon(Icons.swap_vert_outlined), + setKey: SettingBoxKey.fullScreenGestureReverse, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '观看人数', + subtitle: '展示同时在看人数', + leading: const Icon(Icons.people_outlined), + setKey: SettingBoxKey.enableOnlineTotal, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '默认全屏方向', + leading: const Icon(Icons.open_with_outlined), + getSubtitle: () => + '当前全屏方向:${FullScreenMode.values[Pref.fullScreenMode].desc}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '默认全屏方向', + value: Pref.fullScreenMode, + values: FullScreenMode.values.map((e) { + return (e.index, e.desc); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.fullScreenMode, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '底部进度条展示', + leading: const Icon(Icons.border_bottom_outlined), + getSubtitle: () => + '当前展示方式:${BtmProgressBehavior.values[Pref.btmProgressBehavior].desc}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '底部进度条展示', + value: Pref.btmProgressBehavior, + values: BtmProgressBehavior.values.map((e) { + return (e.index, e.desc); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.btmProgressBehavior, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '后台音频服务', + subtitle: '避免画中画没有播放暂停功能', + leading: const Icon(Icons.volume_up_outlined), + setKey: SettingBoxKey.enableBackgroundPlay, + defaultVal: true, + onChanged: (value) { + videoPlayerServiceHandler.enableBackgroundPlay = value; + }, + ), + ]; diff --git a/lib/pages/setting/models/privacy_settings.dart b/lib/pages/setting/models/privacy_settings.dart new file mode 100644 index 000000000..feabef118 --- /dev/null +++ b/lib/pages/setting/models/privacy_settings.dart @@ -0,0 +1,65 @@ +import 'package:PiliPlus/models/common/account_type.dart'; +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/pages/mine/controller.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/utils/accounts.dart'; +import 'package:PiliPlus/utils/accounts/account_manager/account_mgr.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; + +List get privacySettings => [ + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) { + if (!Accounts.main.isLogin) { + SmartDialog.showToast('登录后查看'); + return; + } + Get.toNamed('/blackListPage'); + }, + title: '黑名单管理', + subtitle: '已拉黑用户', + leading: const Icon(Icons.block), + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) { + MineController.onChangeAnonymity(); + setState(); + }, + leading: const Icon(Icons.privacy_tip_outlined), + getTitle: () => MineController.anonymity.value ? '退出无痕模式' : '进入无痕模式', + getSubtitle: () => MineController.anonymity.value + ? '已进入无痕模式,搜索、观看视频/直播不携带Cookie与CSRF,其余操作不受影响' + : '未开启无痕模式,将使用账户信息提供完整服务', + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) { + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('查看详情'), + content: SingleChildScrollView( + child: Text( + AccountManager.apiTypeSet[AccountType.heartbeat]! + .join('\n'), + ), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: const Text('确认'), + ) + ], + ); + }, + ); + }, + leading: const Icon(Icons.flag_outlined), + title: '了解无痕模式', + subtitle: '查看无痕模式作用的API列表', + ), + ]; diff --git a/lib/pages/setting/models/recommend_settings.dart b/lib/pages/setting/models/recommend_settings.dart new file mode 100644 index 000000000..2e92bafdb --- /dev/null +++ b/lib/pages/setting/models/recommend_settings.dart @@ -0,0 +1,126 @@ +import 'package:PiliPlus/http/video.dart'; +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/pages/rcmd/controller.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/utils/recommend_filter.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +List get recommendSettings => [ + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '首页使用app端推荐', + subtitle: '若web端推荐不太符合预期,可尝试切换至app端推荐', + leading: const Icon(Icons.model_training_outlined), + setKey: SettingBoxKey.appRcmd, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '推荐动态', + subtitle: '是否在推荐内容中展示动态(仅app端)', + leading: const Icon(Icons.motion_photos_on_outlined), + setKey: SettingBoxKey.enableRcmdDynamic, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '保留首页推荐刷新', + subtitle: '下拉刷新时保留上次内容', + leading: const Icon(Icons.refresh), + setKey: SettingBoxKey.enableSaveLastData, + defaultVal: false, + onChanged: (value) { + try { + Get.find().enableSaveLastData = value; + } catch (e) { + if (kDebugMode) debugPrint('$e'); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '显示上次看到位置提示', + subtitle: '保留上次推荐时,在上次刷新位置显示提示', + leading: const Icon(Icons.tips_and_updates_outlined), + setKey: SettingBoxKey.savedRcmdTip, + defaultVal: true, + onChanged: (value) { + try { + RcmdController ctr = Get.find() + ..savedRcmdTip = value; + if (!value) { + ctr.lastRefreshAt = null; + } + } catch (e) { + if (kDebugMode) debugPrint('$e'); + } + }, + ), + getVideoFilterSelectModel( + context: Get.context!, + title: '点赞率', + suffix: '%', + key: SettingBoxKey.minLikeRatioForRecommend, + values: [0, 1, 2, 3, 4], + onChanged: (value) => RecommendFilter.minLikeRatioForRecommend = value, + ), + getBanwordModel( + context: Get.context!, + title: '标题关键词过滤', + key: SettingBoxKey.banWordForRecommend, + callback: (value) { + RecommendFilter.rcmdRegExp = value; + RecommendFilter.enableFilter = value.pattern.isNotEmpty; + }, + ), + getBanwordModel( + context: Get.context!, + title: 'App推荐/热门/排行榜: 视频分区关键词过滤', + key: SettingBoxKey.banWordForZone, + callback: (value) { + VideoHttp.zoneRegExp = value; + VideoHttp.enableFilter = value.pattern.isNotEmpty; + }, + ), + getVideoFilterSelectModel( + context: Get.context!, + title: '视频时长', + suffix: 's', + key: SettingBoxKey.minDurationForRcmd, + values: [0, 30, 60, 90, 120], + onChanged: (value) => RecommendFilter.minDurationForRcmd = value, + ), + getVideoFilterSelectModel( + context: Get.context!, + title: '播放量', + key: SettingBoxKey.minPlayForRcmd, + values: [0, 50, 100, 500, 1000], + onChanged: (value) => RecommendFilter.minPlayForRcmd = value, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '已关注UP豁免推荐过滤', + subtitle: '推荐中已关注用户发布的内容不会被过滤', + leading: const Icon(Icons.favorite_border_outlined), + setKey: SettingBoxKey.exemptFilterForFollowed, + defaultVal: true, + onChanged: (value) { + RecommendFilter.exemptFilterForFollowed = value; + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '过滤器也应用于相关视频', + subtitle: '视频详情页的相关视频也进行过滤¹', + leading: const Icon(Icons.explore_outlined), + setKey: SettingBoxKey.applyFilterToRelatedVideos, + defaultVal: true, + onChanged: (value) { + RecommendFilter.applyFilterToRelatedVideos = value; + }, + ), + ]; diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart new file mode 100644 index 000000000..a9719f7df --- /dev/null +++ b/lib/pages/setting/models/style_settings.dart @@ -0,0 +1,658 @@ +import 'dart:io'; + +import 'package:PiliPlus/common/widgets/custom_toast.dart'; +import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/main.dart'; +import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart'; +import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; +import 'package:PiliPlus/models/common/home_tab_type.dart'; +import 'package:PiliPlus/models/common/msg/msg_unread_type.dart'; +import 'package:PiliPlus/models/common/nav_bar_config.dart'; +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/models/common/theme/theme_type.dart'; +import 'package:PiliPlus/pages/main/controller.dart'; +import 'package:PiliPlus/pages/mine/controller.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/pages/color_select.dart'; +import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; +import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart'; +import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; +import 'package:PiliPlus/router/app_pages.dart'; +import 'package:PiliPlus/utils/global_data.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:auto_orientation/auto_orientation.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'; + +List get styleSettings => [ + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '横屏适配', + subtitle: '启用横屏布局与逻辑,平板、折叠屏等可开启;建议全屏方向设为【不改变当前方向】', + leading: const Icon(Icons.phonelink_outlined), + setKey: SettingBoxKey.horizontalScreen, + defaultVal: Pref.horizontalScreen, + onChanged: (value) { + if (value) { + autoScreen(); + } else { + AutoOrientation.portraitUpMode(); + } + }), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '改用侧边栏', + subtitle: '开启后底栏与顶栏被替换,且相关设置失效', + leading: const Icon(Icons.chrome_reader_mode_outlined), + setKey: SettingBoxKey.useSideBar, + defaultVal: false, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: 'App字体字重', + subtitle: '点击设置', + setKey: SettingBoxKey.appFontWeight, + defaultVal: false, + onTap: () { + showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: 'App字体字重', + value: Pref.appFontWeight.toDouble() + 1, + min: 1, + max: FontWeight.values.length.toDouble(), + divisions: FontWeight.values.length - 1, + ); + }, + ).then((res) async { + if (res != null) { + await GStorage.setting + .put(SettingBoxKey.appFontWeight, res.toInt() - 1); + Get.forceAppUpdate(); + } + }); + }, + leading: const Icon(Icons.text_fields), + onChanged: (value) { + Get.forceAppUpdate(); + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '页面过渡动画', + leading: const Icon(Icons.animation), + getSubtitle: () => '当前:${CustomGetPage.pageTransition.name}', + onTap: (setState) async { + Transition? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '页面过渡动画', + value: CustomGetPage.pageTransition, + values: Transition.values.map((e) { + return (e, e.name); + }).toList(), + ); + }, + ); + if (result != null) { + CustomGetPage.pageTransition = result; + await GStorage.setting + .put(SettingBoxKey.pageTransition, result.index); + SmartDialog.showToast('重启生效'); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '优化平板导航栏', + leading: const Icon(MdiIcons.soundbar), + setKey: SettingBoxKey.optTabletNav, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: 'MD3样式底栏', + subtitle: 'Material You设计规范底栏,关闭可变窄', + leading: const Icon(Icons.design_services_outlined), + setKey: SettingBoxKey.enableMYBar, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + double? result = await showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: '列表最大列宽度(默认240dp)', + value: Pref.smallCardWidth, + min: 150.0, + max: 500.0, + divisions: 35, + suffix: 'dp', + ); + }); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.smallCardWidth, result); + SmartDialog.showToast('重启生效'); + setState(); + } + }, + leading: const Icon(Icons.calendar_view_week_outlined), + title: '列表宽度(dp)限制', + getSubtitle: () => + '当前:${Pref.smallCardWidth.toInt()}dp,屏幕宽度:${Get.mediaQuery.size.width.toPrecision(2)}dp。宽度越小列数越多。', + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '视频播放页使用深色主题', + leading: const Icon(Icons.dark_mode_outlined), + setKey: SettingBoxKey.darkVideoPage, + defaultVal: false, + onChanged: (value) { + if (value && MyApp.darkThemeData == null) { + Get.forceAppUpdate(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '播放页移除安全边距', + subtitle: '隐藏状态栏、撑满屏幕,但播放控件仍处于安全域内', + leading: const Icon(Icons.fit_screen_outlined), + setKey: SettingBoxKey.videoPlayerRemoveSafeArea, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '动态页启用瀑布流', + subtitle: '关闭会显示为单列', + leading: const Icon(Icons.view_array_outlined), + setKey: SettingBoxKey.dynamicsWaterfallFlow, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '动态页UP主显示位置', + leading: const Icon(Icons.person_outlined), + getSubtitle: () => '当前:${Pref.upPanelPosition.label}', + onTap: (setState) async { + UpPanelPosition? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '动态页UP主显示位置', + value: Pref.upPanelPosition, + values: UpPanelPosition.values.map((e) { + return (e, e.label); + }).toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.upPanelPosition, result.index); + SmartDialog.showToast('重启生效'); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '动态页显示所有已关注UP主', + subtitle: '并以最常访问排序UP', + leading: const Icon(Icons.people_alt_outlined), + setKey: SettingBoxKey.dynamicsShowAllFollowedUp, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '动态页展开正在直播UP列表', + leading: const Icon(Icons.live_tv), + setKey: SettingBoxKey.expandDynLivePanel, + defaultVal: false, + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + DynamicBadgeMode? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '动态未读标记', + value: Pref.dynamicBadgeType, + values: DynamicBadgeMode.values.map((e) { + return (e, e.desc); + }).toList(), + ); + }, + ); + if (result != null) { + MainController mainController = Get.put(MainController()) + ..dynamicBadgeMode = DynamicBadgeMode.values[result.index]; + if (mainController.dynamicBadgeMode != DynamicBadgeMode.hidden) { + mainController.getUnreadDynamic(); + } + await GStorage.setting + .put(SettingBoxKey.dynamicBadgeMode, result.index); + SmartDialog.showToast('设置成功'); + setState(); + } + }, + title: '动态未读标记', + leading: const Icon(Icons.motion_photos_on_outlined), + getSubtitle: () => '当前标记样式:${Pref.dynamicBadgeType.desc}', + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + DynamicBadgeMode? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '消息未读标记', + value: Pref.msgBadgeMode, + values: DynamicBadgeMode.values.map((e) { + return (e, e.desc); + }).toList(), + ); + }, + ); + if (result != null) { + MainController mainController = Get.put(MainController()) + ..msgBadgeMode = DynamicBadgeMode.values[result.index]; + if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { + mainController.queryUnreadMsg(true); + } else { + mainController.msgUnReadCount.value = ''; + } + await GStorage.setting + .put(SettingBoxKey.msgBadgeMode, result.index); + SmartDialog.showToast('设置成功'); + setState(); + } + }, + title: '消息未读标记', + leading: const Icon(MdiIcons.bellBadgeOutline), + getSubtitle: () => '当前标记样式:${Pref.msgBadgeMode.desc}', + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + final result = await showDialog>( + context: Get.context!, + builder: (context) { + return MultiSelectDialog( + title: '消息未读类型', + initValues: Pref.msgUnReadTypeV2, + values: {for (var i in MsgUnReadType.values) i: i.title}, + ); + }, + ); + if (result != null) { + MainController mainController = Get.put(MainController()) + ..msgUnReadTypes = result; + if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { + mainController.queryUnreadMsg(); + } + await GStorage.setting.put(SettingBoxKey.msgUnReadTypeV2, + result.map((item) => item.index).toList()..sort()); + SmartDialog.showToast('设置成功'); + setState(); + } + }, + title: '消息未读类型', + leading: const Icon(MdiIcons.bellCogOutline), + getSubtitle: () => + '当前消息类型:${Pref.msgUnReadTypeV2.map((item) => item.title).join('、')}', + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '首页顶栏收起', + subtitle: '首页列表滑动时,收起顶栏', + leading: const Icon(Icons.vertical_align_top_outlined), + setKey: SettingBoxKey.hideSearchBar, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '首页底栏收起', + subtitle: '首页列表滑动时,收起底栏', + leading: const Icon(Icons.vertical_align_bottom_outlined), + setKey: SettingBoxKey.hideTabBar, + defaultVal: true, + needReboot: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '降低收起/展开顶/底栏频率', + leading: const Icon(Icons.vertical_distribute), + setKey: SettingBoxKey.navSearchStreamDebounce, + defaultVal: false, + onChanged: (value) { + try { + Get.find().navSearchStreamDebounce = value; + Get.forceAppUpdate(); + } catch (_) {} + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) { + _showQualityDialog( + context: Get.context!, + title: '图片质量', + initValue: Pref.picQuality, + callback: (picQuality) async { + GlobalData().imgQuality = picQuality; + await GStorage.setting + .put(SettingBoxKey.defaultPicQa, picQuality); + setState(); + }, + ); + }, + title: '图片质量', + subtitle: '选择合适的图片清晰度,上限100%', + leading: const Icon(Icons.image_outlined), + getTrailing: () => Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + '${Pref.picQuality}%', + style: Get.theme.textTheme.titleSmall, + ), + ), + ), + // preview quality + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) { + _showQualityDialog( + context: Get.context!, + title: '查看大图质量', + initValue: Pref.previewQ, + callback: (picQuality) async { + await GStorage.setting + .put(SettingBoxKey.previewQuality, picQuality); + setState(); + }, + ); + }, + title: '查看大图质量', + subtitle: '选择合适的图片清晰度,上限100%', + leading: const Icon(Icons.image_outlined), + getTrailing: () => Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + '${Pref.previewQ}%', + style: Get.theme.textTheme.titleSmall, + ), + ), + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + double? result = await showDialog( + context: Get.context!, + builder: (context) { + return SlideDialog( + title: 'Toast不透明度', + value: CustomToast.toastOpacity, + min: 0.0, + max: 1.0, + divisions: 10, + ); + }, + ); + if (result != null) { + CustomToast.toastOpacity = result; + await GStorage.setting.put(SettingBoxKey.defaultToastOp, result); + SmartDialog.showToast('设置成功'); + setState(); + } + }, + leading: const Icon(Icons.opacity_outlined), + title: '气泡提示不透明度', + subtitle: '自定义气泡提示(Toast)不透明度', + getTrailing: () => Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + CustomToast.toastOpacity.toStringAsFixed(1), + style: Get.theme.textTheme.titleSmall, + ), + ), + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + ThemeType? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '主题模式', + value: Pref.themeType, + values: ThemeType.values.map( + (e) { + return (e, e.desc); + }, + ).toList()); + }, + ); + if (result != null) { + try { + Get.find().themeType.value = result; + } catch (_) {} + GStorage.setting.put(SettingBoxKey.themeMode, result.index); + Get.put(ColorSelectController()).themeType.value = result; + Get.changeThemeMode(result.toThemeMode); + setState(); + } + }, + leading: const Icon(Icons.flashlight_on_outlined), + title: '主题模式', + getSubtitle: () => '当前模式:${Pref.themeType.desc}', + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + leading: const Icon(Icons.invert_colors), + title: '纯黑主题', + setKey: SettingBoxKey.isPureBlackTheme, + defaultVal: false, + onChanged: (value) { + if (Get.theme.brightness == Brightness.dark || Pref.darkVideoPage) { + Get.forceAppUpdate(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) => Get.toNamed('/colorSetting'), + leading: const Icon(Icons.color_lens_outlined), + title: '应用主题', + getSubtitle: () => + '当前主题:${Get.put(ColorSelectController()).type.value == 0 ? '动态取色' : '指定颜色'}', + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '首页启动页', + value: Pref.defaultHomePage, + values: NavigationBarType.values.map((e) { + return (e.index, e.label); + }).toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.defaultHomePage, result); + SmartDialog.showToast('设置成功,重启生效'); + setState(); + } + }, + leading: const Icon(Icons.home_outlined), + title: '默认启动页', + getSubtitle: () => + '当前启动页:${NavigationBarType.values.firstWhere((e) => e.index == Pref.defaultHomePage).label}', + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '滑动动画弹簧参数', + leading: const Icon(Icons.chrome_reader_mode_outlined), + onTap: (setState) { + final numberRegExp = RegExp(r'[\d\.]+'); + List springDescription = CustomSpringDescription + .springDescription + .map((i) => i.toString()) + .toList(); + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('弹簧参数'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: List.generate( + 3, + (index) => TextFormField( + autofocus: index == 0, + initialValue: springDescription[index], + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + onChanged: (value) { + springDescription[index] = value; + }, + inputFormatters: [ + FilteringTextInputFormatter.allow(numberRegExp) + ], + decoration: InputDecoration( + labelText: const [ + 'mass', + 'stiffness', + 'damping' + ][index], + ), + ), + ), + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () async { + Get.back(); + await GStorage.setting.put( + SettingBoxKey.springDescription, + List.generate( + 3, + (i) => + double.tryParse(springDescription[i]) ?? + CustomSpringDescription.springDescription[i], + ), + ); + SmartDialog.showToast('设置成功,重启生效'); + setState(); + }, + child: const Text('确定'), + ) + ], + ); + }, + ); + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) async { + var result = await Get.toNamed('/fontSizeSetting'); + if (result != null) { + Get.put(ColorSelectController()).currentTextScale.value = result; + } + }, + title: '字体大小', + leading: const Icon(Icons.format_size_outlined), + getSubtitle: () => + Get.put(ColorSelectController()).currentTextScale.value == 1.0 + ? '默认' + : Get.put(ColorSelectController()) + .currentTextScale + .value + .toString(), + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) => Get.toNamed('/barSetting', arguments: { + 'key': SettingBoxKey.tabBarSort, + 'defaultBars': HomeTabType.values, + 'title': '首页标签页' + }), + title: '首页标签页', + subtitle: '删除或调换首页标签页', + leading: const Icon(Icons.toc_outlined), + ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) => Get.toNamed('/barSetting', arguments: { + 'key': SettingBoxKey.navBarSort, + 'defaultBars': NavigationBarType.values, + 'title': 'Navbar' + }), + title: 'Navbar编辑', + subtitle: '删除或调换Navbar', + leading: const Icon(Icons.toc_outlined), + ), + if (Platform.isAndroid) + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) => Get.toNamed('/displayModeSetting'), + title: '屏幕帧率', + leading: const Icon(Icons.autofps_select_outlined), + ), + ]; + +void _showQualityDialog({ + required BuildContext context, + required String title, + required int initValue, + required ValueChanged callback, +}) { + showDialog( + context: context, + builder: (context) => SlideDialog( + value: initValue.toDouble(), + title: title, + min: 10, + max: 100, + divisions: 9, + suffix: '%', + precise: 0), + ).then((result) { + if (result != null) { + SmartDialog.showToast('设置成功'); + callback(result.toInt()); + } + }); +} diff --git a/lib/pages/setting/models/video_settings.dart b/lib/pages/setting/models/video_settings.dart new file mode 100644 index 000000000..a9bd7c799 --- /dev/null +++ b/lib/pages/setting/models/video_settings.dart @@ -0,0 +1,377 @@ +import 'dart:io'; + +import 'package:PiliPlus/http/video.dart'; +import 'package:PiliPlus/models/common/settings_type.dart'; +import 'package:PiliPlus/models/common/video/audio_quality.dart'; +import 'package:PiliPlus/models/common/video/cdn_type.dart'; +import 'package:PiliPlus/models/common/video/live_quality.dart'; +import 'package:PiliPlus/models/common/video/video_decode_type.dart'; +import 'package:PiliPlus/models/common/video/video_quality.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:PiliPlus/utils/video_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; + +List get videoSettings => [ + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '开启硬解', + subtitle: '以较低功耗播放视频,若异常卡死请关闭', + leading: const Icon(Icons.flash_on_outlined), + setKey: SettingBoxKey.enableHA, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '免登录1080P', + subtitle: '免登录查看1080P视频', + leading: const Icon(Icons.hd_outlined), + setKey: SettingBoxKey.p1080, + defaultVal: true, + onChanged: (value) { + VideoHttp.p1080 = value; + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: 'B站定向流量支持', + subtitle: '若套餐含B站定向流量,则会自动使用。可查阅运营商的流量记录确认。', + leading: const Icon(Icons.perm_data_setting_outlined), + getTrailing: () => Transform.scale( + alignment: Alignment.centerRight, + scale: 0.8, + child: Switch( + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { + if (states.isNotEmpty && states.first == WidgetState.selected) { + return const Icon(Icons.lock_outline_rounded); + } + return null; + }), + value: true, + onChanged: (_) {}, + ), + ), + ), + SettingsModel( + settingsType: SettingsType.normal, + title: 'CDN 设置', + leading: const Icon(MdiIcons.cloudPlusOutline), + getSubtitle: () => + '当前使用:${CDNService.fromCode(VideoUtils.cdnService).desc},部分 CDN 可能失效,如无法播放请尝试切换', + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + return const CdnSelectDialog(); + }, + ); + if (result != null) { + VideoUtils.cdnService = result; + await GStorage.setting.put(SettingBoxKey.CDNService, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: 'CDN 测速', + leading: const Icon(Icons.speed), + subtitle: '测速通过模拟加载视频实现,注意流量消耗,结果仅供参考', + setKey: SettingBoxKey.cdnSpeedTest, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '音频不跟随 CDN 设置', + subtitle: '直接采用备用 URL,可解决部分视频无声', + leading: const Icon(MdiIcons.musicNotePlus), + setKey: SettingBoxKey.disableAudioCDN, + defaultVal: true, + onChanged: (value) { + VideoUtils.disableAudioCDN = value; + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '默认画质', + leading: const Icon(Icons.video_settings_outlined), + getSubtitle: () => + '当前画质:${VideoQuality.fromCode(Pref.defaultVideoQa).desc}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '默认画质', + value: Pref.defaultVideoQa, + values: VideoQuality.values.reversed + .map((e) => (e.code, e.desc)) + .toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.defaultVideoQa, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '蜂窝网络画质', + leading: const Icon(Icons.video_settings_outlined), + getSubtitle: () => + '当前画质:${VideoQuality.fromCode(Pref.defaultVideoQaCellular).desc}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '蜂窝网络画质', + value: Pref.defaultVideoQaCellular, + values: VideoQuality.values.reversed.map((e) { + return (e.code, e.desc); + }).toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.defaultVideoQaCellular, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '默认音质', + leading: const Icon(Icons.music_video_outlined), + getSubtitle: () => + '当前音质:${AudioQuality.fromCode(Pref.defaultAudioQa).desc}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '默认音质', + value: Pref.defaultAudioQa, + values: AudioQuality.values.reversed + .map((e) => (e.code, e.desc)) + .toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.defaultAudioQa, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '蜂窝网络音质', + leading: const Icon(Icons.music_video_outlined), + getSubtitle: () => + '当前音质:${AudioQuality.fromCode(Pref.defaultAudioQaCellular).desc}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '蜂窝网络音质', + value: Pref.defaultAudioQaCellular, + values: AudioQuality.values.reversed.map((e) { + return (e.code, e.desc); + }).toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.defaultAudioQaCellular, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '直播默认画质', + leading: const Icon(Icons.video_settings_outlined), + getSubtitle: () => + '当前画质:${LiveQualityExt.fromCode(Pref.liveQuality)!.description}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '直播默认画质', + value: Pref.liveQuality, + values: LiveQuality.values + .map((e) => (e.code, e.description)) + .toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.liveQuality, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '蜂窝网络直播默认画质', + leading: const Icon(Icons.video_settings_outlined), + getSubtitle: () => + '当前画质:${LiveQualityExt.fromCode(Pref.liveQualityCellular)!.description}', + onTap: (setState) async { + int? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '直播默认画质', + value: Pref.liveQualityCellular, + values: LiveQuality.values.map((e) { + return (e.code, e.description); + }).toList(), + ); + }, + ); + if (result != null) { + await GStorage.setting + .put(SettingBoxKey.liveQualityCellular, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '首选解码格式', + leading: const Icon(Icons.movie_creation_outlined), + getSubtitle: () => + '首选解码格式:${VideoDecodeFormatTypeExt.fromCode(Pref.defaultDecode)!.description},请根据设备支持情况与需求调整', + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '默认解码格式', + value: Pref.defaultDecode, + values: VideoDecodeFormatType.values + .map((e) => (e.code, e.description)) + .toList()); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.defaultDecode, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '次选解码格式', + getSubtitle: () => + '非杜比视频次选:${VideoDecodeFormatTypeExt.fromCode(Pref.secondDecode)!.description},仍无则选择首个提供的解码格式', + leading: const Icon(Icons.swap_horizontal_circle_outlined), + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '次选解码格式', + value: Pref.secondDecode, + values: VideoDecodeFormatType.values.map((e) { + return (e.code, e.description); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.secondDecode, result); + setState(); + } + }, + ), + if (Platform.isAndroid) + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '优先使用 OpenSL ES 输出音频', + leading: const Icon(Icons.speaker_outlined), + subtitle: + '关闭则优先使用AudioTrack输出音频(此项即mpv的--ao),若遇系统音效丢失、无声、音画不同步等问题请尝试关闭。', + setKey: SettingBoxKey.useOpenSLES, + defaultVal: true, + ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '扩大缓冲区', + leading: const Icon(Icons.storage_outlined), + subtitle: '默认缓冲区为视频4MB/直播16MB,开启后为32MB/64MB,加载时间变长', + setKey: SettingBoxKey.expandBuffer, + defaultVal: false, + ), + //video-sync + SettingsModel( + settingsType: SettingsType.normal, + title: '视频同步', + leading: const Icon(Icons.view_timeline_outlined), + getSubtitle: () => '当前:${Pref.videoSync}(此项即mpv的--video-sync)', + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '视频同步', + value: Pref.videoSync, + values: [ + 'audio', + 'display-resample', + 'display-resample-vdrop', + 'display-resample-desync', + 'display-tempo', + 'display-vdrop', + 'display-adrop', + 'display-desync', + 'desync' + ].map((e) { + return (e, e); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.videoSync, result); + setState(); + } + }, + ), + SettingsModel( + settingsType: SettingsType.normal, + title: '硬解模式', + leading: const Icon(Icons.memory_outlined), + getSubtitle: () => '当前:${Pref.hardwareDecoding}(此项即mpv的--hwdec)', + onTap: (setState) async { + String? result = await showDialog( + context: Get.context!, + builder: (context) { + return SelectDialog( + title: '硬解模式', + value: Pref.hardwareDecoding, + values: + ['auto', 'auto-copy', 'auto-safe', 'no', 'yes'].map((e) { + return (e, e); + }).toList()); + }, + ); + if (result != null) { + await GStorage.setting.put(SettingBoxKey.hardwareDecoding, result); + setState(); + } + }, + ), + ]; diff --git a/lib/pages/setting/pages/color_select.dart b/lib/pages/setting/pages/color_select.dart index 9d5dc7c79..b08c353bc 100644 --- a/lib/pages/setting/pages/color_select.dart +++ b/lib/pages/setting/pages/color_select.dart @@ -6,6 +6,8 @@ import 'package:PiliPlus/pages/home/view.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flex_seed_scheme/flex_seed_scheme.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -42,7 +44,7 @@ List generateItems(int count) { class _ColorSelectPageState extends State { final ColorSelectController ctr = Get.put(ColorSelectController()); FlexSchemeVariant _dynamicSchemeVariant = - FlexSchemeVariant.values[GStorage.schemeVariant]; + FlexSchemeVariant.values[Pref.schemeVariant]; @override Widget build(BuildContext context) { @@ -64,9 +66,8 @@ class _ColorSelectPageState extends State { return SelectDialog( title: '主题模式', value: ctr.themeType.value, - values: ThemeType.values - .map((e) => (e, e.description)) - .toList()); + values: + ThemeType.values.map((e) => (e, e.desc)).toList()); }, ); if (result != null) { @@ -84,8 +85,7 @@ class _ColorSelectPageState extends State { child: const Icon(Icons.flashlight_on_outlined), ), title: Text('主题模式', style: titleStyle), - subtitle: Obx(() => Text( - '当前模式:${ctr.themeType.value.description}', + subtitle: Obx(() => Text('当前模式:${ctr.themeType.value.desc}', style: subTitleStyle)), ), Obx( @@ -155,8 +155,9 @@ class _ColorSelectPageState extends State { title: const Text('动态取色'), groupValue: ctr.type.value, onChanged: (dynamic val) { - ctr.type.value = 0; - ctr.setting.put(SettingBoxKey.dynamicColor, true); + ctr + ..type.value = 0 + ..setting.put(SettingBoxKey.dynamicColor, true); Get.forceAppUpdate(); }, ), @@ -167,8 +168,9 @@ class _ColorSelectPageState extends State { title: const Text('指定颜色'), groupValue: ctr.type.value, onChanged: (dynamic val) { - ctr.type.value = 1; - ctr.setting.put(SettingBoxKey.dynamicColor, false); + ctr + ..type.value = 1 + ..setting.put(SettingBoxKey.dynamicColor, false); Get.forceAppUpdate(); }, ), @@ -254,23 +256,11 @@ class _ColorSelectPageState extends State { } class ColorSelectController extends GetxController { - RxBool dynamicColor = true.obs; - RxInt type = 0.obs; - RxInt currentColor = 0.obs; - RxDouble currentTextScale = 1.0.obs; - Rx themeType = GStorage.themeType.obs; + final RxBool dynamicColor = Pref.dynamicColor.obs; + late final RxInt type = (dynamicColor.value ? 0 : 1).obs; + final RxInt currentColor = Pref.customColor.obs; + final RxDouble currentTextScale = Pref.defaultTextScale.obs; + final Rx themeType = Pref.themeType.obs; - Box get setting => GStorage.setting; - - @override - void onInit() { - dynamicColor.value = - setting.get(SettingBoxKey.dynamicColor, defaultValue: true); - type.value = dynamicColor.value ? 0 : 1; - currentColor.value = - setting.get(SettingBoxKey.customColor, defaultValue: 0); - currentTextScale.value = - setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0); - super.onInit(); - } + Box setting = GStorage.setting; } diff --git a/lib/pages/setting/pages/display_mode.dart b/lib/pages/setting/pages/display_mode.dart index 70699bdd3..e9294d6b0 100644 --- a/lib/pages/setting/pages/display_mode.dart +++ b/lib/pages/setting/pages/display_mode.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show PlatformException; @@ -18,7 +19,7 @@ class _SetDisplayModeState extends State { DisplayMode? active; DisplayMode? preferred; - Box get setting => GStorage.setting; + Box setting = GStorage.setting; @override void initState() { diff --git a/lib/pages/setting/pages/font_size_select.dart b/lib/pages/setting/pages/font_size_select.dart index 79bf9aa8e..7cbbaa274 100644 --- a/lib/pages/setting/pages/font_size_select.dart +++ b/lib/pages/setting/pages/font_size_select.dart @@ -1,7 +1,8 @@ import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; class FontSizeSelectPage extends StatefulWidget { const FontSizeSelectPage({super.key}); @@ -12,23 +13,12 @@ class FontSizeSelectPage extends StatefulWidget { class _FontSizeSelectPageState extends State { List list = List.generate(16, (index) => 0.85 + index * 0.05); - late double minSize; - late double maxSize; - late double currentSize; - - Box get setting => GStorage.setting; - - @override - void initState() { - super.initState(); - minSize = list.first; - maxSize = list.last; - currentSize = - setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0); - } + late double minSize = list.first; + late double maxSize = list.last; + double currentSize = Pref.defaultTextScale; void setFontSize() { - setting.put(SettingBoxKey.defaultTextScale, currentSize); + GStorage.setting.put(SettingBoxKey.defaultTextScale, currentSize); Get ..back(result: currentSize) ..forceAppUpdate(); diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart index 84d67da81..209fbe1ff 100644 --- a/lib/pages/setting/pages/logs.dart +++ b/lib/pages/setting/pages/logs.dart @@ -4,6 +4,8 @@ import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; import 'package:PiliPlus/services/loggeer.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -21,7 +23,7 @@ class _LogsPageState extends State { late String fileContent; List logsContent = []; DateTime? latestLog; - late bool enableLog = GStorage.enableLog; + late bool enableLog = Pref.enableLog; @override void initState() { diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index 50d433a54..78310109c 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -2,6 +2,8 @@ import 'dart:math'; import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -16,10 +18,10 @@ class PlaySpeedPage extends StatefulWidget { } class _PlaySpeedPageState extends State { - late double playSpeedDefault; - late double longPressSpeedDefault; - late List speedList; - late bool enableAutoLongPressSpeed; + late double playSpeedDefault = Pref.playSpeedDefault; + late double longPressSpeedDefault = Pref.longPressSpeedDefault; + late List speedList = Pref.speedList; + late bool enableAutoLongPressSpeed = Pref.enableAutoLongPressSpeed; List<({int id, String title, Icon icon})> sheetMenu = [ ( id: 1, @@ -47,22 +49,7 @@ class _PlaySpeedPageState extends State { ), ]; - Box get video => GStorage.video; - - @override - void initState() { - super.initState(); - // 默认倍速 - playSpeedDefault = - video.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); - // 默认长按倍速 - longPressSpeedDefault = - video.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 3.0); - // 倍速 - speedList = GStorage.speedList; - enableAutoLongPressSpeed = GStorage.setting - .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); - } + Box video = GStorage.video; // 添加自定义倍速 void onAddSpeed() { @@ -199,7 +186,7 @@ class _PlaySpeedPageState extends State { TextButton( onPressed: () async { await video.delete(VideoBoxKey.speedsList); - speedList = GStorage.speedList; + speedList = Pref.speedList; setState(() {}); }, child: const Text('重置'), diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 87fe64e71..ddd42c222 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -1,5 +1,5 @@ -import 'package:PiliPlus/pages/setting/widgets/model.dart'; -import 'package:PiliPlus/services/service_locator.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/models/play_settings.dart'; import 'package:flutter/material.dart'; class PlaySetting extends StatefulWidget { @@ -12,13 +12,7 @@ class PlaySetting extends StatefulWidget { } class _PlaySettingState extends State { - @override - void dispose() { - super.dispose(); - - // 重新验证媒体通知后台播放设置 - videoPlayerServiceHandler.revalidateSetting(); - } + final settings = playSettings; @override Widget build(BuildContext context) { @@ -27,10 +21,10 @@ class _PlaySettingState extends State { ? null : AppBar(title: const Text('播放器设置')), body: ListView( - children: [ - ...playSettings.map((item) => item.widget), - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), - ], + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + children: settings.map((item) => item.widget).toList(), ), ); } diff --git a/lib/pages/setting/privacy_setting.dart b/lib/pages/setting/privacy_setting.dart index 1f1f64446..e9faf1095 100644 --- a/lib/pages/setting/privacy_setting.dart +++ b/lib/pages/setting/privacy_setting.dart @@ -1,20 +1,29 @@ -import 'package:PiliPlus/pages/setting/widgets/model.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/models/privacy_settings.dart'; import 'package:flutter/material.dart'; -class PrivacySetting extends StatelessWidget { +class PrivacySetting extends StatefulWidget { const PrivacySetting({super.key, this.showAppBar}); final bool? showAppBar; + @override + State createState() => _PrivacySettingState(); +} + +class _PrivacySettingState extends State { + final settings = privacySettings; + @override Widget build(BuildContext context) { return Scaffold( - appBar: showAppBar == false ? null : AppBar(title: const Text('隐私设置')), + appBar: + widget.showAppBar == false ? null : AppBar(title: const Text('隐私设置')), body: ListView( - children: [ - ...privacySettings.map((item) => item.widget), - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), - ], + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + children: settings.map((item) => item.widget).toList(), ), ); } diff --git a/lib/pages/setting/recommend_setting.dart b/lib/pages/setting/recommend_setting.dart index 9aee7203d..c7ed2505f 100644 --- a/lib/pages/setting/recommend_setting.dart +++ b/lib/pages/setting/recommend_setting.dart @@ -1,19 +1,42 @@ -import 'package:PiliPlus/pages/setting/widgets/model.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/models/recommend_settings.dart'; import 'package:flutter/material.dart'; -class RecommendSetting extends StatelessWidget { +class RecommendSetting extends StatefulWidget { const RecommendSetting({super.key, this.showAppBar}); final bool? showAppBar; + @override + State createState() => _RecommendSettingState(); +} + +class _RecommendSettingState extends State { + final list = recommendSettings; + late final List part; + + @override + void initState() { + super.initState(); + part = list.sublist(0, 4); + list.removeRange(0, 4); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( - appBar: showAppBar == false ? null : AppBar(title: const Text('推荐流设置')), + appBar: widget.showAppBar == false + ? null + : AppBar(title: const Text('推荐流设置')), body: ListView( + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), children: [ - ...recommendSettings.map((item) => item.widget), + ...part.map((item) => item.widget), + const Divider(height: 1), + ...list.map((item) => item.widget), ListTile( dense: true, subtitle: Text( @@ -25,7 +48,6 @@ class RecommendSetting extends StatelessWidget { color: theme.colorScheme.outline.withValues(alpha: 0.7)), ), ), - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), ], ), ); diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 160319c9b..c81f3e73c 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -1,20 +1,29 @@ -import 'package:PiliPlus/pages/setting/widgets/model.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/models/style_settings.dart'; import 'package:flutter/material.dart'; -class StyleSetting extends StatelessWidget { +class StyleSetting extends StatefulWidget { const StyleSetting({super.key, this.showAppBar}); final bool? showAppBar; + @override + State createState() => _StyleSettingState(); +} + +class _StyleSettingState extends State { + final settings = styleSettings; + @override Widget build(BuildContext context) { return Scaffold( - appBar: showAppBar == false ? null : AppBar(title: const Text('外观设置')), + appBar: + widget.showAppBar == false ? null : AppBar(title: const Text('外观设置')), body: ListView( - children: [ - ...styleSettings.map((item) => item.widget), - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), - ], + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + children: settings.map((item) => item.widget).toList(), ), ); } diff --git a/lib/pages/setting/video_setting.dart b/lib/pages/setting/video_setting.dart index 2dd347dfe..ac72e6288 100644 --- a/lib/pages/setting/video_setting.dart +++ b/lib/pages/setting/video_setting.dart @@ -1,20 +1,30 @@ -import 'package:PiliPlus/pages/setting/widgets/model.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/models/video_settings.dart'; import 'package:flutter/material.dart'; -class VideoSetting extends StatelessWidget { +class VideoSetting extends StatefulWidget { const VideoSetting({super.key, this.showAppBar}); final bool? showAppBar; + @override + State createState() => _VideoSettingState(); +} + +class _VideoSettingState extends State { + final settings = videoSettings; + @override Widget build(BuildContext context) { return Scaffold( - appBar: showAppBar == false ? null : AppBar(title: const Text('音视频设置')), + appBar: widget.showAppBar == false + ? null + : AppBar(title: const Text('音视频设置')), body: ListView( - children: [ - ...videoSettings.map((item) => item.widget), - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), - ], + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + children: settings.map((item) => item.widget).toList(), ), ); } diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index ee32209aa..d964d6085 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -9,9 +9,9 @@ import 'package:PiliPlus/pages/setting/style_setting.dart'; import 'package:PiliPlus/pages/setting/video_setting.dart'; import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart'; import 'package:PiliPlus/pages/webdav/view.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart deleted file mode 100644 index 98bdb6c53..000000000 --- a/lib/pages/setting/widgets/model.dart +++ /dev/null @@ -1,2720 +0,0 @@ -import 'dart:io'; -import 'dart:math'; - -import 'package:PiliPlus/common/widgets/custom_icon.dart'; -import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; -import 'package:PiliPlus/common/widgets/refresh_indicator.dart' - show kDragContainerExtentPercentage, displacement; -import 'package:PiliPlus/http/reply.dart'; -import 'package:PiliPlus/http/video.dart'; -import 'package:PiliPlus/main.dart'; -import 'package:PiliPlus/models/common/account_type.dart'; -import 'package:PiliPlus/models/common/audio_normalization.dart'; -import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart'; -import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; -import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; -import 'package:PiliPlus/models/common/home_tab_type.dart'; -import 'package:PiliPlus/models/common/member/tab_type.dart'; -import 'package:PiliPlus/models/common/msg/msg_unread_type.dart'; -import 'package:PiliPlus/models/common/nav_bar_config.dart'; -import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; -import 'package:PiliPlus/models/common/settings_type.dart'; -import 'package:PiliPlus/models/common/super_resolution_type.dart'; -import 'package:PiliPlus/models/common/theme/theme_type.dart'; -import 'package:PiliPlus/models/common/video/audio_quality.dart'; -import 'package:PiliPlus/models/common/video/cdn_type.dart'; -import 'package:PiliPlus/models/common/video/live_quality.dart'; -import 'package:PiliPlus/models/common/video/subtitle_pref_type.dart'; -import 'package:PiliPlus/models/common/video/video_decode_type.dart'; -import 'package:PiliPlus/models/common/video/video_quality.dart'; -import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/pages/home/controller.dart'; -import 'package:PiliPlus/pages/hot/controller.dart'; -import 'package:PiliPlus/pages/main/controller.dart'; -import 'package:PiliPlus/pages/mine/controller.dart'; -import 'package:PiliPlus/pages/rcmd/controller.dart'; -import 'package:PiliPlus/pages/setting/pages/color_select.dart'; -import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart'; -import 'package:PiliPlus/pages/setting/widgets/normal_item.dart'; -import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; -import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart'; -import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; -import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; -import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart'; -import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; -import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; -import 'package:PiliPlus/utils/accounts/account_manager/account_mgr.dart'; -import 'package:PiliPlus/utils/cache_manage.dart'; -import 'package:PiliPlus/utils/feed_back.dart'; -import 'package:PiliPlus/utils/global_data.dart'; -import 'package:PiliPlus/utils/recommend_filter.dart'; -import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/update.dart'; -import 'package:auto_orientation/auto_orientation.dart'; -import 'package:flutter/foundation.dart' show kDebugMode; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' show FilteringTextInputFormatter; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:get/get.dart'; -import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; - -extension SettingsModelExt on SettingsModel { - Widget get widget => switch (settingsType) { - SettingsType.divider => const Divider(height: 1), - SettingsType.normal => NormalItem( - title: title, - getTitle: getTitle, - subtitle: subtitle, - getSubtitle: getSubtitle, - setKey: setKey, - defaultVal: defaultVal, - onChanged: onChanged, - needReboot: needReboot, - leading: leading, - getTrailing: getTrailing, - onTap: onTap, - contentPadding: contentPadding, - titleStyle: titleStyle, - ), - SettingsType.sw1tch => SetSwitchItem( - title: title, - subtitle: subtitle, - setKey: setKey, - defaultVal: defaultVal, - onChanged: onChanged, - needReboot: needReboot, - leading: leading, - onTap: onTap, - contentPadding: contentPadding, - titleStyle: titleStyle, - ), - }; -} - -class SettingsModel { - final SettingsType settingsType; - final String? title; - final Function? getTitle; - final String? subtitle; - final Function? getSubtitle; - final String? setKey; - final bool? defaultVal; - final ValueChanged? onChanged; - final bool? needReboot; - final Widget? leading; - final Function? getTrailing; - final Function? onTap; - final EdgeInsetsGeometry? contentPadding; - final TextStyle? titleStyle; - - SettingsModel({ - required this.settingsType, - this.title, - this.getTitle, - this.subtitle, - this.getSubtitle, - this.setKey, - this.defaultVal, - this.onChanged, - this.needReboot, - this.leading, - this.getTrailing, - this.onTap, - this.contentPadding, - this.titleStyle, - }); -} - -List get styleSettings => [ - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '横屏适配', - subtitle: '启用横屏布局与逻辑,平板、折叠屏等可开启;建议全屏方向设为【不改变当前方向】', - leading: const Icon(Icons.phonelink_outlined), - setKey: SettingBoxKey.horizontalScreen, - defaultVal: GStorage.horizontalScreen, - onChanged: (value) { - if (value) { - autoScreen(); - } else { - AutoOrientation.portraitUpMode(); - } - }), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '改用侧边栏', - subtitle: '开启后底栏与顶栏被替换,且相关设置失效', - leading: const Icon(Icons.chrome_reader_mode_outlined), - setKey: SettingBoxKey.useSideBar, - defaultVal: false, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: 'App字体字重', - subtitle: '点击设置', - setKey: SettingBoxKey.appFontWeight, - defaultVal: false, - onTap: () { - showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: 'App字体字重', - value: GStorage.appFontWeight.toDouble() + 1, - min: 1, - max: FontWeight.values.length.toDouble(), - divisions: FontWeight.values.length - 1, - ); - }, - ).then((res) async { - if (res != null) { - await GStorage.setting - .put(SettingBoxKey.appFontWeight, res.toInt() - 1); - Get.forceAppUpdate(); - } - }); - }, - leading: const Icon(Icons.text_fields), - onChanged: (value) { - Get.forceAppUpdate(); - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '页面过渡动画', - leading: const Icon(Icons.animation), - getSubtitle: () => '当前:${GStorage.pageTransition.name}', - onTap: (setState) async { - Transition? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '页面过渡动画', - value: GStorage.pageTransition, - values: Transition.values.map((e) { - return (e, e.name); - }).toList(), - ); - }, - ); - if (result != null) { - GStorage.pageTransition = result; - await GStorage.setting - .put(SettingBoxKey.pageTransition, result.index); - SmartDialog.showToast('重启生效'); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '优化平板导航栏', - leading: const Icon(MdiIcons.soundbar), - setKey: SettingBoxKey.optTabletNav, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: 'MD3样式底栏', - subtitle: 'Material You设计规范底栏,关闭可变窄', - leading: const Icon(Icons.design_services_outlined), - setKey: SettingBoxKey.enableMYBar, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - double? result = await showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: '列表最大列宽度(默认240dp)', - value: GStorage.smallCardWidth, - min: 150.0, - max: 500.0, - divisions: 35, - suffix: 'dp', - ); - }); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.smallCardWidth, result); - SmartDialog.showToast('重启生效'); - setState(); - } - }, - leading: const Icon(Icons.calendar_view_week_outlined), - title: '列表宽度(dp)限制', - getSubtitle: () => - '当前:${GStorage.smallCardWidth.toInt()}dp,屏幕宽度:${Get.mediaQuery.size.width.toPrecision(2)}dp。宽度越小列数越多。', - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '视频播放页使用深色主题', - leading: const Icon(Icons.dark_mode_outlined), - setKey: SettingBoxKey.darkVideoPage, - defaultVal: false, - onChanged: (value) { - if (value && MyApp.darkThemeData == null) { - Get.forceAppUpdate(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '播放页移除安全边距', - subtitle: '隐藏状态栏、撑满屏幕,但播放控件仍处于安全域内', - leading: const Icon(Icons.fit_screen_outlined), - setKey: SettingBoxKey.videoPlayerRemoveSafeArea, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '动态页启用瀑布流', - subtitle: '关闭会显示为单列', - leading: const Icon(Icons.view_array_outlined), - setKey: SettingBoxKey.dynamicsWaterfallFlow, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '动态页UP主显示位置', - leading: const Icon(Icons.person_outlined), - getSubtitle: () => '当前:${GStorage.upPanelPosition.label}', - onTap: (setState) async { - UpPanelPosition? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '动态页UP主显示位置', - value: GStorage.upPanelPosition, - values: UpPanelPosition.values.map((e) { - return (e, e.label); - }).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.upPanelPosition, result.index); - SmartDialog.showToast('重启生效'); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '动态页显示所有已关注UP主', - subtitle: '并以最常访问排序UP', - leading: const Icon(Icons.people_alt_outlined), - setKey: SettingBoxKey.dynamicsShowAllFollowedUp, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '动态页展开正在直播UP列表', - leading: const Icon(Icons.live_tv), - setKey: SettingBoxKey.expandDynLivePanel, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - DynamicBadgeMode? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '动态未读标记', - value: GStorage.dynamicBadgeType, - values: DynamicBadgeMode.values.map((e) { - return (e, e.description); - }).toList(), - ); - }, - ); - if (result != null) { - MainController mainController = Get.put(MainController()) - ..dynamicBadgeMode = DynamicBadgeMode.values[result.index]; - if (mainController.dynamicBadgeMode != DynamicBadgeMode.hidden) { - mainController.getUnreadDynamic(); - } - await GStorage.setting - .put(SettingBoxKey.dynamicBadgeMode, result.index); - SmartDialog.showToast('设置成功'); - setState(); - } - }, - title: '动态未读标记', - leading: const Icon(Icons.motion_photos_on_outlined), - getSubtitle: () => '当前标记样式:${GStorage.dynamicBadgeType.description}', - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - DynamicBadgeMode? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '消息未读标记', - value: GStorage.msgBadgeMode, - values: DynamicBadgeMode.values.map((e) { - return (e, e.description); - }).toList(), - ); - }, - ); - if (result != null) { - MainController mainController = Get.put(MainController()) - ..msgBadgeMode = DynamicBadgeMode.values[result.index]; - if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { - mainController.queryUnreadMsg(true); - } else { - mainController.msgUnReadCount.value = ''; - } - await GStorage.setting - .put(SettingBoxKey.msgBadgeMode, result.index); - SmartDialog.showToast('设置成功'); - setState(); - } - }, - title: '消息未读标记', - leading: const Icon(MdiIcons.bellBadgeOutline), - getSubtitle: () => '当前标记样式:${GStorage.msgBadgeMode.description}', - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - final result = await showDialog>( - context: Get.context!, - builder: (context) { - return MultiSelectDialog( - title: '消息未读类型', - initValues: GStorage.msgUnReadTypeV2, - values: {for (var i in MsgUnReadType.values) i: i.title}, - ); - }, - ); - if (result != null) { - MainController mainController = Get.put(MainController()) - ..msgUnReadTypes = result; - if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) { - mainController.queryUnreadMsg(); - } - await GStorage.setting.put(SettingBoxKey.msgUnReadTypeV2, - result.map((item) => item.index).toList()..sort()); - SmartDialog.showToast('设置成功'); - setState(); - } - }, - title: '消息未读类型', - leading: const Icon(MdiIcons.bellCogOutline), - getSubtitle: () => - '当前消息类型:${GStorage.msgUnReadTypeV2.map((item) => item.title).join('、')}', - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '首页顶栏收起', - subtitle: '首页列表滑动时,收起顶栏', - leading: const Icon(Icons.vertical_align_top_outlined), - setKey: SettingBoxKey.hideSearchBar, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '首页底栏收起', - subtitle: '首页列表滑动时,收起底栏', - leading: const Icon(Icons.vertical_align_bottom_outlined), - setKey: SettingBoxKey.hideTabBar, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '降低收起/展开顶/底栏频率', - leading: const Icon(Icons.vertical_distribute), - setKey: SettingBoxKey.navSearchStreamDebounce, - defaultVal: false, - onChanged: (value) { - try { - Get.find().navSearchStreamDebounce = value; - Get.forceAppUpdate(); - } catch (_) {} - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) { - _showQualityDialog( - context: Get.context!, - title: '图片质量', - initValue: GStorage.picQuality, - callback: (picQuality) async { - GlobalData().imgQuality = picQuality; - await GStorage.setting - .put(SettingBoxKey.defaultPicQa, picQuality); - setState(); - }, - ); - }, - title: '图片质量', - subtitle: '选择合适的图片清晰度,上限100%', - leading: const Icon(Icons.image_outlined), - getTrailing: () => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - '${GStorage.picQuality}%', - style: Get.theme.textTheme.titleSmall, - ), - ), - ), - // preview quality - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) { - _showQualityDialog( - context: Get.context!, - title: '查看大图质量', - initValue: GStorage.previewQ, - callback: (picQuality) async { - await GStorage.setting - .put(SettingBoxKey.previewQuality, picQuality); - setState(); - }, - ); - }, - title: '查看大图质量', - subtitle: '选择合适的图片清晰度,上限100%', - leading: const Icon(Icons.image_outlined), - getTrailing: () => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - '${GStorage.previewQ}%', - style: Get.theme.textTheme.titleSmall, - ), - ), - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - double? result = await showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: 'Toast不透明度', - value: GStorage.toastOpacity, - min: 0.0, - max: 1.0, - divisions: 10, - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultToastOp, result); - SmartDialog.showToast('设置成功'); - setState(); - } - }, - leading: const Icon(Icons.opacity_outlined), - title: '气泡提示不透明度', - subtitle: '自定义气泡提示(Toast)不透明度', - getTrailing: () => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - GStorage.toastOpacity.toStringAsFixed(1), - style: Get.theme.textTheme.titleSmall, - ), - ), - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - ThemeType? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '主题模式', - value: GStorage.themeType, - values: ThemeType.values.map( - (e) { - return (e, e.description); - }, - ).toList()); - }, - ); - if (result != null) { - try { - Get.find().themeType.value = result; - } catch (_) {} - GStorage.setting.put(SettingBoxKey.themeMode, result.index); - Get.put(ColorSelectController()).themeType.value = result; - Get.changeThemeMode(result.toThemeMode); - setState(); - } - }, - leading: const Icon(Icons.flashlight_on_outlined), - title: '主题模式', - getSubtitle: () => '当前模式:${GStorage.themeType.description}', - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - leading: const Icon(Icons.invert_colors), - title: '纯黑主题', - setKey: SettingBoxKey.isPureBlackTheme, - defaultVal: false, - onChanged: (value) { - if (Get.theme.brightness == Brightness.dark || - GStorage.darkVideoPage) { - Get.forceAppUpdate(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) => Get.toNamed('/colorSetting'), - leading: const Icon(Icons.color_lens_outlined), - title: '应用主题', - getSubtitle: () => - '当前主题:${Get.put(ColorSelectController()).type.value == 0 ? '动态取色' : '指定颜色'}', - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '首页启动页', - value: GStorage.defaultHomePage, - values: NavigationBarType.values.map((e) { - return (e.index, e.label); - }).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultHomePage, result); - SmartDialog.showToast('设置成功,重启生效'); - setState(); - } - }, - leading: const Icon(Icons.home_outlined), - title: '默认启动页', - getSubtitle: () => - '当前启动页:${NavigationBarType.values.firstWhere((e) => e.index == GStorage.defaultHomePage).label}', - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '滑动动画弹簧参数', - leading: const Icon(Icons.chrome_reader_mode_outlined), - onTap: (setState) { - final numberRegExp = RegExp(r'[\d\.]+'); - List springDescription = - GStorage.springDescription.map((i) => i.toString()).toList(); - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('弹簧参数'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: List.generate( - 3, - (index) => TextFormField( - autofocus: index == 0, - initialValue: springDescription[index], - keyboardType: - const TextInputType.numberWithOptions(decimal: true), - onChanged: (value) { - springDescription[index] = value; - }, - inputFormatters: [ - FilteringTextInputFormatter.allow(numberRegExp) - ], - decoration: InputDecoration( - labelText: const [ - 'mass', - 'stiffness', - 'damping' - ][index], - ), - ), - ), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - await GStorage.setting.put( - SettingBoxKey.springDescription, - List.generate( - 3, - (i) => - double.tryParse(springDescription[i]) ?? - GStorage.springDescription[i], - ), - ); - SmartDialog.showToast('设置成功,重启生效'); - setState(); - }, - child: const Text('确定'), - ) - ], - ); - }, - ); - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) async { - var result = await Get.toNamed('/fontSizeSetting'); - if (result != null) { - Get.put(ColorSelectController()).currentTextScale.value = result; - } - }, - title: '字体大小', - leading: const Icon(Icons.format_size_outlined), - getSubtitle: () => - Get.put(ColorSelectController()).currentTextScale.value == 1.0 - ? '默认' - : Get.put(ColorSelectController()) - .currentTextScale - .value - .toString(), - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) => Get.toNamed('/barSetting', arguments: { - 'key': SettingBoxKey.tabBarSort, - 'defaultBars': HomeTabType.values, - 'title': '首页标签页' - }), - title: '首页标签页', - subtitle: '删除或调换首页标签页', - leading: const Icon(Icons.toc_outlined), - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) => Get.toNamed('/barSetting', arguments: { - 'key': SettingBoxKey.navBarSort, - 'defaultBars': NavigationBarType.values, - 'title': 'Navbar' - }), - title: 'Navbar编辑', - subtitle: '删除或调换Navbar', - leading: const Icon(Icons.toc_outlined), - ), - if (Platform.isAndroid) - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) => Get.toNamed('/displayModeSetting'), - title: '屏幕帧率', - leading: const Icon(Icons.autofps_select_outlined), - ), - ]; - -void _showQualityDialog({ - required BuildContext context, - required String title, - required int initValue, - required ValueChanged callback, -}) { - showDialog( - context: context, - builder: (context) => SlideDialog( - value: initValue.toDouble(), - title: title, - min: 10, - max: 100, - divisions: 9, - suffix: '%', - precise: 0), - ).then((result) { - if (result != null) { - SmartDialog.showToast('设置成功'); - callback(result.toInt()); - } - }); -} - -List get playSettings => [ - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '弹幕开关', - subtitle: '是否展示弹幕', - leading: const Icon(CustomIcon.dm_settings), - setKey: SettingBoxKey.enableShowDanmaku, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) => Get.toNamed('/playSpeedSet'), - leading: const Icon(Icons.speed_outlined), - title: '倍速设置', - subtitle: '设置视频播放速度', - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '自动播放', - subtitle: '进入详情页自动播放', - leading: const Icon(Icons.motion_photos_auto_outlined), - setKey: SettingBoxKey.autoPlayEnable, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '双击快退/快进', - subtitle: '左侧双击快退/右侧双击快进,关闭则双击均为暂停/播放', - leading: const Icon(Icons.touch_app_outlined), - setKey: SettingBoxKey.enableQuickDouble, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '左右侧滑动调节亮度/音量', - leading: const Icon(MdiIcons.tuneVerticalVariant), - setKey: SettingBoxKey.enableSlideVolumeBrightness, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '中间滑动进入/退出全屏', - leading: const Icon(MdiIcons.panVertical), - setKey: SettingBoxKey.enableSlideFS, - defaultVal: true, - ), - _getVideoFilterSelectModel( - context: Get.context!, - title: '双击快进/快退时长', - suffix: 's', - key: SettingBoxKey.fastForBackwardDuration, - values: [5, 10, 15], - defaultValue: 10, - isFilter: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '滑动快进/快退使用相对时长', - leading: const Icon(Icons.swap_horiz_outlined), - setKey: SettingBoxKey.useRelativeSlide, - defaultVal: false, - ), - _getVideoFilterSelectModel( - context: Get.context!, - title: '滑动快进/快退时长', - subtitle: '从播放器一端滑到另一端的快进/快退时长', - suffix: GStorage.useRelativeSlide ? '%' : 's', - key: SettingBoxKey.sliderDuration, - values: [25, 50, 90, 100], - defaultValue: 90, - isFilter: false, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '自动启用字幕', - leading: const Icon(Icons.closed_caption_outlined), - getSubtitle: () => - '当前选择偏好:${SubtitlePrefTypeExt.fromCode(GStorage.defaultSubtitlePreference)!.description}', - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '字幕选择偏好', - value: GStorage.setting.get(SettingBoxKey.subtitlePreference, - defaultValue: SubtitlePrefType.values.first.code), - values: SubtitlePrefType.values - .map((e) => (e.code, e.description)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.subtitlePreference, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '竖屏扩大展示', - subtitle: '小屏竖屏视频宽高比由16:9扩大至1:1(不支持收起);横屏适配时,扩大至9:16', - leading: const Icon(Icons.expand_outlined), - setKey: SettingBoxKey.enableVerticalExpand, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '自动全屏', - subtitle: '视频开始播放时进入全屏', - leading: const Icon(Icons.fullscreen_outlined), - setKey: SettingBoxKey.enableAutoEnter, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '自动退出全屏', - subtitle: '视频结束播放时退出全屏', - leading: const Icon(Icons.fullscreen_exit_outlined), - setKey: SettingBoxKey.enableAutoExit, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '延长播放控件显示时间', - subtitle: '开启后延长至30秒,便于屏幕阅读器滑动切换控件焦点', - leading: const Icon(Icons.timer_outlined), - setKey: SettingBoxKey.enableLongShowControl, - defaultVal: false), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '全向旋转', - subtitle: '小屏可受重力转为临时全屏,若系统锁定旋转仍触发请关闭,关闭会影响横屏适配', - leading: const Icon(Icons.screen_rotation_alt_outlined), - setKey: SettingBoxKey.allowRotateScreen, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '后台播放', - subtitle: '进入后台时继续播放', - leading: const Icon(Icons.motion_photos_pause_outlined), - setKey: SettingBoxKey.continuePlayInBackground, - defaultVal: false, - ), - if (Platform.isAndroid) - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '后台画中画', - subtitle: '进入后台时以小窗形式(PiP)播放', - leading: const Icon(Icons.picture_in_picture_outlined), - setKey: SettingBoxKey.autoPiP, - defaultVal: false, - onChanged: (val) { - if (val && - !GStorage.setting.get(SettingBoxKey.enableBackgroundPlay, - defaultValue: true)) { - SmartDialog.showToast('建议开启后台音频服务'); - } - }), - if (Platform.isAndroid) - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '画中画不加载弹幕', - subtitle: '当弹幕开关开启时,小窗屏蔽弹幕以获得较好的体验', - leading: const Icon(Icons.subtitles_off_outlined), - setKey: SettingBoxKey.pipNoDanmaku, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '全屏手势反向', - subtitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出', - leading: const Icon(Icons.swap_vert_outlined), - setKey: SettingBoxKey.fullScreenGestureReverse, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '观看人数', - subtitle: '展示同时在看人数', - leading: const Icon(Icons.people_outlined), - setKey: SettingBoxKey.enableOnlineTotal, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '默认全屏方向', - leading: const Icon(Icons.open_with_outlined), - getSubtitle: () => - '当前全屏方向:${FullScreenModeCode.fromCode(GStorage.defaultFullScreenMode).description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '默认全屏方向', - value: GStorage.defaultFullScreenMode, - values: FullScreenMode.values.map((e) { - return (e.code, e.description); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.fullScreenMode, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '底部进度条展示', - leading: const Icon(Icons.border_bottom_outlined), - getSubtitle: () => - '当前展示方式:${BtmProgresBehaviorCode.fromCode(GStorage.defaultBtmProgressBehavior).description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '底部进度条展示', - value: GStorage.defaultBtmProgressBehavior, - values: BtmProgressBehavior.values.map((e) { - return (e.code, e.description); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.btmProgressBehavior, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '后台音频服务', - subtitle: '避免画中画没有播放暂停功能', - leading: const Icon(Icons.volume_up_outlined), - setKey: SettingBoxKey.enableBackgroundPlay, - defaultVal: true, - ), - ]; - -List get videoSettings => [ - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '开启硬解', - subtitle: '以较低功耗播放视频,若异常卡死请关闭', - leading: const Icon(Icons.flash_on_outlined), - setKey: SettingBoxKey.enableHA, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '免登录1080P', - subtitle: '免登录查看1080P视频', - leading: const Icon(Icons.hd_outlined), - setKey: SettingBoxKey.p1080, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: 'B站定向流量支持', - subtitle: '若套餐含B站定向流量,则会自动使用。可查阅运营商的流量记录确认。', - leading: const Icon(Icons.perm_data_setting_outlined), - getTrailing: () => Transform.scale( - alignment: Alignment.centerRight, - scale: 0.8, - child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { - if (states.isNotEmpty && states.first == WidgetState.selected) { - return const Icon(Icons.lock_outline_rounded); - } - return null; - }), - value: true, - onChanged: (_) {}, - ), - ), - ), - SettingsModel( - settingsType: SettingsType.normal, - title: 'CDN 设置', - leading: const Icon(MdiIcons.cloudPlusOutline), - getSubtitle: () => - '当前使用:${CDNService.fromCode(GStorage.defaultCDNService).description},部分 CDN 可能失效,如无法播放请尝试切换', - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - return const CdnSelectDialog(); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.CDNService, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: 'CDN 测速', - leading: const Icon(Icons.speed), - subtitle: '测速通过模拟加载视频实现,注意流量消耗,结果仅供参考', - setKey: SettingBoxKey.cdnSpeedTest, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '音频不跟随 CDN 设置', - subtitle: '直接采用备用 URL,可解决部分视频无声', - leading: const Icon(MdiIcons.musicNotePlus), - setKey: SettingBoxKey.disableAudioCDN, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '默认画质', - leading: const Icon(Icons.video_settings_outlined), - getSubtitle: () => - '当前画质:${VideoQuality.fromCode(GStorage.defaultVideoQa).description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '默认画质', - value: GStorage.defaultVideoQa, - values: VideoQuality.values.reversed - .map((e) => (e.code, e.description)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultVideoQa, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '蜂窝网络画质', - leading: const Icon(Icons.video_settings_outlined), - getSubtitle: () => - '当前画质:${VideoQuality.fromCode(GStorage.defaultVideoQaCellular).description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '蜂窝网络画质', - value: GStorage.defaultVideoQaCellular, - values: VideoQuality.values.reversed.map((e) { - return (e.code, e.description); - }).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.defaultVideoQaCellular, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '默认音质', - leading: const Icon(Icons.music_video_outlined), - getSubtitle: () => - '当前音质:${AudioQuality.fromCode(GStorage.defaultAudioQa).description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '默认音质', - value: GStorage.defaultAudioQa, - values: AudioQuality.values.reversed - .map((e) => (e.code, e.description)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultAudioQa, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '蜂窝网络音质', - leading: const Icon(Icons.music_video_outlined), - getSubtitle: () => - '当前音质:${AudioQuality.fromCode(GStorage.defaultAudioQaCellular).description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '蜂窝网络音质', - value: GStorage.defaultAudioQaCellular, - values: AudioQuality.values.reversed.map((e) { - return (e.code, e.description); - }).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.defaultAudioQaCellular, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '直播默认画质', - leading: const Icon(Icons.video_settings_outlined), - getSubtitle: () => - '当前画质:${LiveQualityExt.fromCode(GStorage.liveQuality)!.description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '直播默认画质', - value: GStorage.liveQuality, - values: LiveQuality.values - .map((e) => (e.code, e.description)) - .toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.liveQuality, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '蜂窝网络直播默认画质', - leading: const Icon(Icons.video_settings_outlined), - getSubtitle: () => - '当前画质:${LiveQualityExt.fromCode(GStorage.liveQualityCellular)!.description}', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '直播默认画质', - value: GStorage.liveQualityCellular, - values: LiveQuality.values.map((e) { - return (e.code, e.description); - }).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.liveQualityCellular, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '首选解码格式', - leading: const Icon(Icons.movie_creation_outlined), - getSubtitle: () => - '首选解码格式:${VideoDecodeFormatTypeExt.fromCode(GStorage.defaultDecode)!.description},请根据设备支持情况与需求调整', - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '默认解码格式', - value: GStorage.defaultDecode, - values: VideoDecodeFormatType.values - .map((e) => (e.code, e.description)) - .toList()); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.defaultDecode, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '次选解码格式', - getSubtitle: () => - '非杜比视频次选:${VideoDecodeFormatTypeExt.fromCode(GStorage.secondDecode)!.description},仍无则选择首个提供的解码格式', - leading: const Icon(Icons.swap_horizontal_circle_outlined), - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '次选解码格式', - value: GStorage.secondDecode, - values: VideoDecodeFormatType.values.map((e) { - return (e.code, e.description); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.secondDecode, result); - setState(); - } - }, - ), - if (Platform.isAndroid) - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '优先使用 OpenSL ES 输出音频', - leading: const Icon(Icons.speaker_outlined), - subtitle: - '关闭则优先使用AudioTrack输出音频(此项即mpv的--ao),若遇系统音效丢失、无声、音画不同步等问题请尝试关闭。', - setKey: SettingBoxKey.useOpenSLES, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '扩大缓冲区', - leading: const Icon(Icons.storage_outlined), - subtitle: '默认缓冲区为视频4MB/直播16MB,开启后为32MB/64MB,加载时间变长', - setKey: SettingBoxKey.expandBuffer, - defaultVal: false, - ), - //video-sync - SettingsModel( - settingsType: SettingsType.normal, - title: '视频同步', - leading: const Icon(Icons.view_timeline_outlined), - getSubtitle: () => '当前:${GStorage.videoSync}(此项即mpv的--video-sync)', - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '视频同步', - value: GStorage.videoSync, - values: [ - 'audio', - 'display-resample', - 'display-resample-vdrop', - 'display-resample-desync', - 'display-tempo', - 'display-vdrop', - 'display-adrop', - 'display-desync', - 'desync' - ].map((e) { - return (e, e); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.videoSync, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '硬解模式', - leading: const Icon(Icons.memory_outlined), - getSubtitle: () => '当前:${GStorage.hardwareDecoding}(此项即mpv的--hwdec)', - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '硬解模式', - value: GStorage.hardwareDecoding, - values: - ['auto', 'auto-copy', 'auto-safe', 'no', 'yes'].map((e) { - return (e, e); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.hardwareDecoding, result); - setState(); - } - }, - ), - ]; - -List get recommendSettings => [ - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '首页使用app端推荐', - subtitle: '若web端推荐不太符合预期,可尝试切换至app端推荐', - leading: const Icon(Icons.model_training_outlined), - setKey: SettingBoxKey.appRcmd, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '推荐动态', - subtitle: '是否在推荐内容中展示动态(仅app端)', - leading: const Icon(Icons.motion_photos_on_outlined), - setKey: SettingBoxKey.enableRcmdDynamic, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '保留首页推荐刷新', - subtitle: '下拉刷新时保留上次内容', - leading: const Icon(Icons.refresh), - setKey: SettingBoxKey.enableSaveLastData, - defaultVal: false, - onChanged: (value) { - try { - Get.find().enableSaveLastData = value; - } catch (e) { - if (kDebugMode) debugPrint('$e'); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示上次看到位置提示', - subtitle: '保留上次推荐时,在上次刷新位置显示提示', - leading: const Icon(Icons.tips_and_updates_outlined), - setKey: SettingBoxKey.savedRcmdTip, - defaultVal: true, - onChanged: (value) { - try { - RcmdController ctr = Get.find() - ..savedRcmdTip = value; - if (!value) { - ctr.lastRefreshAt = null; - } - } catch (e) { - if (kDebugMode) debugPrint('$e'); - } - }, - ), - SettingsModel(settingsType: SettingsType.divider), - _getVideoFilterSelectModel( - context: Get.context!, - title: '点赞率', - suffix: '%', - key: SettingBoxKey.minLikeRatioForRecommend, - values: [0, 1, 2, 3, 4], - ), - _getBanwordModel( - context: Get.context!, - title: '标题关键词过滤', - key: SettingBoxKey.banWordForRecommend, - callback: (value) { - RecommendFilter.rcmdRegExp = value; - }, - ), - _getBanwordModel( - context: Get.context!, - title: 'App推荐/热门/排行榜: 视频分区关键词过滤', - key: SettingBoxKey.banWordForZone, - callback: (value) { - VideoHttp.zoneRegExp = value; - }, - ), - _getVideoFilterSelectModel( - context: Get.context!, - title: '视频时长', - suffix: 's', - key: SettingBoxKey.minDurationForRcmd, - values: [0, 30, 60, 90, 120], - ), - _getVideoFilterSelectModel( - context: Get.context!, - title: '播放量', - key: SettingBoxKey.minPlayForRcmd, - values: [0, 50, 100, 500, 1000], - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '已关注UP豁免推荐过滤', - subtitle: '推荐中已关注用户发布的内容不会被过滤', - leading: const Icon(Icons.favorite_border_outlined), - setKey: SettingBoxKey.exemptFilterForFollowed, - defaultVal: true, - onChanged: (value) { - RecommendFilter.exemptFilterForFollowed = value; - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '过滤器也应用于相关视频', - subtitle: '视频详情页的相关视频也进行过滤¹', - leading: const Icon(Icons.explore_outlined), - setKey: SettingBoxKey.applyFilterToRelatedVideos, - defaultVal: true, - onChanged: (value) { - RecommendFilter.applyFilterToRelatedVideos = value; - }, - ), - ]; - -List get privacySettings => [ - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) { - if (!Accounts.main.isLogin) { - SmartDialog.showToast('登录后查看'); - return; - } - Get.toNamed('/blackListPage'); - }, - title: '黑名单管理', - subtitle: '已拉黑用户', - leading: const Icon(Icons.block), - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) { - MineController.onChangeAnonymity(); - setState(); - }, - leading: const Icon(Icons.privacy_tip_outlined), - getTitle: () => MineController.anonymity.value ? '退出无痕模式' : '进入无痕模式', - getSubtitle: () => MineController.anonymity.value - ? '已进入无痕模式,搜索、观看视频/直播不携带Cookie与CSRF,其余操作不受影响' - : '未开启无痕模式,将使用账户信息提供完整服务', - ), - SettingsModel( - settingsType: SettingsType.normal, - onTap: (setState) { - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('查看详情'), - content: SingleChildScrollView( - child: Text( - AccountManager.apiTypeSet[AccountType.heartbeat]! - .join('\n'), - ), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - leading: const Icon(Icons.flag_outlined), - title: '了解无痕模式', - subtitle: '查看无痕模式作用的API列表', - ), - ]; - -List get extraSettings => [ - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '空降助手', - subtitle: '点击配置', - setKey: SettingBoxKey.enableSponsorBlock, - defaultVal: false, - onTap: () => Get.toNamed('/sponsorBlock'), - leading: const Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Icon(Icons.shield_outlined), - Icon(Icons.play_arrow_rounded, size: 15), - ], - ), - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '检查未读动态', - subtitle: '点击设置检查周期(min)', - leading: const Icon(Icons.notifications_none), - setKey: SettingBoxKey.checkDynamic, - defaultVal: true, - onChanged: (value) { - Get.find().checkDynamic = value; - }, - onTap: () { - int dynamicPeriod = GStorage.dynamicPeriod; - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('检查周期'), - content: TextFormField( - autofocus: true, - initialValue: dynamicPeriod.toString(), - keyboardType: TextInputType.number, - onChanged: (value) { - dynamicPeriod = int.tryParse(value) ?? 5; - }, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'\d+')), - ], - decoration: const InputDecoration(suffixText: 'min'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () { - Get.back(); - GStorage.setting - .put(SettingBoxKey.dynamicPeriod, dynamicPeriod); - Get.find().dynamicPeriod = dynamicPeriod; - }, - child: const Text('确定'), - ) - ], - ); - }, - ); - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示视频分段信息', - leading: Transform.rotate( - angle: pi / 2, - child: const Icon(MdiIcons.viewHeadline), - ), - setKey: SettingBoxKey.showViewPoints, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '视频页显示相关视频', - leading: const Icon(MdiIcons.motionPlayOutline), - setKey: SettingBoxKey.showRelatedVideo, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示视频评论', - leading: const Icon(MdiIcons.commentTextOutline), - setKey: SettingBoxKey.showVideoReply, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示番剧评论', - leading: const Icon(MdiIcons.commentTextOutline), - setKey: SettingBoxKey.showBangumiReply, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '默认展开视频简介', - leading: const Icon(Icons.expand_more), - setKey: SettingBoxKey.alwaysExapndIntroPanel, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '横屏自动展开视频简介', - leading: const Icon(Icons.expand_more), - setKey: SettingBoxKey.exapndIntroPanelH, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '横屏分P/合集列表显示在Tab栏', - leading: const Icon(Icons.format_list_numbered_rtl_sharp), - setKey: SettingBoxKey.horizontalSeasonPanel, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '横屏播放页在侧栏打开UP主页', - leading: const Icon(Icons.account_circle_outlined), - setKey: SettingBoxKey.horizontalMemberPage, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '评论折叠行数', - subtitle: '0行为不折叠', - leading: const Icon(Icons.compress), - setKey: SettingBoxKey.replyLengthLimit, - getTrailing: () => Text( - '${GlobalData().replyLengthLimit}行', - style: Get.theme.textTheme.titleSmall, - ), - onTap: (setState) { - String replyLengthLimit = GlobalData().replyLengthLimit.toString(); - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('评论折叠行数'), - content: TextFormField( - autofocus: true, - initialValue: replyLengthLimit, - keyboardType: TextInputType.number, - onChanged: (value) { - replyLengthLimit = value; - }, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'\d+')), - ], - decoration: const InputDecoration(suffixText: '行'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - GlobalData().replyLengthLimit = - int.tryParse(replyLengthLimit) ?? 6; - await GStorage.setting.put( - SettingBoxKey.replyLengthLimit, - GlobalData().replyLengthLimit, - ); - setState(); - }, - child: const Text('确定'), - ) - ], - ); - }, - ); - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '弹幕行高', - subtitle: '默认1.6', - setKey: SettingBoxKey.danmakuLineHeight, - leading: const Icon(Icons.subtitles_outlined), - getTrailing: () => Text( - GStorage.danmakuLineHeight.toString(), - style: Get.theme.textTheme.titleSmall, - ), - onTap: (setState) { - String danmakuLineHeight = GStorage.danmakuLineHeight.toString(); - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('弹幕行高'), - content: TextFormField( - autofocus: true, - initialValue: danmakuLineHeight, - keyboardType: - const TextInputType.numberWithOptions(decimal: true), - onChanged: (value) { - danmakuLineHeight = value; - }, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - await GStorage.setting.put( - SettingBoxKey.danmakuLineHeight, - max( - 1.0, - double.tryParse(danmakuLineHeight)?.toPrecision(1) ?? - 1.6, - ), - ); - setState(); - }, - child: const Text('确定'), - ) - ], - ); - }, - ); - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示视频警告/争议信息', - leading: const Icon(Icons.warning_amber_rounded), - setKey: SettingBoxKey.showArgueMsg, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '分P/合集:倒序播放从首集开始播放', - subtitle: '开启则自动切换为倒序首集,否则保持当前集', - leading: const Icon(MdiIcons.sort), - setKey: SettingBoxKey.reverseFromFirst, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '禁用 SSL 证书验证', - subtitle: '谨慎开启,禁用容易受到中间人攻击', - leading: const Icon(Icons.security), - needReboot: true, - setKey: SettingBoxKey.badCertificateCallback, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示继续播放分P提示', - leading: const Icon(Icons.local_parking), - setKey: SettingBoxKey.continuePlayingPart, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '横屏在侧栏打开图片预览', - leading: const Icon(Icons.photo_outlined), - setKey: SettingBoxKey.horizontalPreview, - defaultVal: false, - ), - _getBanwordModel( - context: Get.context!, - title: '评论关键词过滤', - key: SettingBoxKey.banWordForReply, - callback: (value) { - ReplyHttp.replyRegExp = value; - }, - ), - _getBanwordModel( - context: Get.context!, - title: '动态关键词过滤', - key: SettingBoxKey.banWordForDyn, - callback: (value) { - DynamicsDataModel.banWordForDyn = value; - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '使用外部浏览器打开链接', - leading: const Icon(Icons.open_in_browser), - setKey: SettingBoxKey.openInBrowser, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '刷新滑动距离', - leading: const Icon(Icons.refresh), - setKey: SettingBoxKey.refreshDragPercentage, - getSubtitle: () => '当前滑动距离: ${GStorage.refreshDragPercentage}x', - onTap: (setState) async { - double? result = await showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: '刷新滑动距离', - min: 0.1, - max: 0.5, - divisions: 8, - precise: 2, - value: GStorage.refreshDragPercentage, - suffix: 'x', - ); - }, - ); - if (result != null) { - kDragContainerExtentPercentage = result; - await GStorage.setting - .put(SettingBoxKey.refreshDragPercentage, result); - Get.forceAppUpdate(); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '刷新指示器高度', - leading: const Icon(Icons.height), - setKey: SettingBoxKey.refreshDisplacement, - getSubtitle: () => '当前指示器高度: ${GStorage.refreshDisplacement}', - onTap: (setState) async { - double? result = await showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: '刷新指示器高度', - min: 10.0, - max: 100.0, - divisions: 9, - value: GStorage.refreshDisplacement, - ); - }, - ); - if (result != null) { - displacement = result; - await GStorage.setting - .put(SettingBoxKey.refreshDisplacement, result); - Get.forceAppUpdate(); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示会员彩色弹幕', - leading: const Icon(MdiIcons.gradientHorizontal), - setKey: SettingBoxKey.showVipDanmaku, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示高级弹幕', - leading: const Icon(MdiIcons.paletteAdvanced), - setKey: SettingBoxKey.showSpecialDanmaku, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '合并弹幕', - subtitle: '合并一段时间内获取到的相同弹幕', - leading: const Icon(Icons.merge), - setKey: SettingBoxKey.mergeDanmaku, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示热门推荐', - subtitle: '热门页面显示每周必看等推荐内容入口', - leading: const Icon(Icons.local_fire_department_outlined), - setKey: SettingBoxKey.showHotRcmd, - defaultVal: false, - onChanged: (value) { - try { - Get.find().showHotRcmd.value = value; - } catch (_) {} - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '音量均衡', - setKey: SettingBoxKey.audioNormalization, - leading: const Icon(Icons.multitrack_audio), - getSubtitle: () { - String audioNormalization = GStorage.audioNormalization; - audioNormalization = switch (audioNormalization) { - '0' => AudioNormalization.disable.title, - '1' => AudioNormalization.dynaudnorm.title, - '2' => AudioNormalization.loudnorm.title, - _ => audioNormalization, - }; - return '当前:「$audioNormalization」'; - }, - onTap: (setState) async { - String? result = await showDialog( - context: Get.context!, - builder: (context) { - String audioNormalization = GStorage.audioNormalization; - Set values = {'0', '1', '2', audioNormalization, '3'}; - return SelectDialog( - title: '音量均衡', - value: audioNormalization, - values: values - .map((e) => ( - e, - switch (e) { - '0' => AudioNormalization.disable.title, - '1' => AudioNormalization.dynaudnorm.title, - '2' => AudioNormalization.loudnorm.title, - '3' => AudioNormalization.custom.title, - _ => e, - } - )) - .toList()); - }, - ); - if (result != null) { - if (result == '3') { - String param = ''; - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('自定义参数'), - content: TextField( - autofocus: true, - onChanged: (value) => param = value, - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - await GStorage.setting - .put(SettingBoxKey.audioNormalization, param); - setState(); - }, - child: const Text('确定'), - ), - ], - ); - }); - } else { - await GStorage.setting - .put(SettingBoxKey.audioNormalization, result); - setState(); - } - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '超分辨率', - leading: const Icon(Icons.stay_current_landscape_outlined), - getSubtitle: () => - '当前:「${SuperResolutionType.values[GStorage.superResolutionType].title}」\n默认设置对番剧生效, 其他视频默认关闭\n超分辨率需要启用硬件解码, 若启用硬件解码后仍然不生效, 尝试切换硬件解码器为 auto-copy', - onTap: (setState) async { - SuperResolutionType? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '超分辨率', - value: - SuperResolutionType.values[GStorage.superResolutionType], - values: SuperResolutionType.values.map((e) { - return (e, e.title); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.superResolutionType, result.index); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '提前初始化播放器', - subtitle: '相对减少手动播放加载时间', - leading: const Icon(Icons.play_circle_outlined), - setKey: SettingBoxKey.preInitPlayer, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '首页切换页面动画', - leading: const Icon(Icons.home_outlined), - setKey: SettingBoxKey.mainTabBarView, - defaultVal: false, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '搜索建议', - leading: const Icon(Icons.search), - setKey: SettingBoxKey.searchSuggestion, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '记录搜索历史', - leading: const Icon(Icons.history), - setKey: SettingBoxKey.recordSearchHistory, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '展示头像/评论/动态装饰', - leading: const Icon(MdiIcons.stickerCircleOutline), - setKey: SettingBoxKey.showDynDecorate, - defaultVal: true, - onChanged: (value) => PendantAvatar.showDynDecorate = value, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '预览 Live Photo', - subtitle: '开启则以视频形式预览 Live Photo,否则预览静态图片', - leading: const Icon(Icons.image_outlined), - setKey: SettingBoxKey.enableLivePhoto, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '滑动跳转预览视频缩略图', - leading: const Icon(Icons.preview_outlined), - setKey: SettingBoxKey.showSeekPreview, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '显示高能进度条', - subtitle: '高能进度条反应了在时域上,单位时间内弹幕发送量的变化趋势', - leading: const Icon(Icons.show_chart), - setKey: SettingBoxKey.showDmChart, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '发评反诈', - subtitle: '发送评论后检查评论是否可见', - leading: const Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Icon(Icons.shield_outlined), - Icon(Icons.reply, size: 14), - ], - ), - setKey: SettingBoxKey.enableCommAntifraud, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '使用「哔哩发评反诈」检查评论', - subtitle: '仅对Android生效', - leading: const Icon( - FontAwesomeIcons.b, - size: 22, - ), - setKey: SettingBoxKey.biliSendCommAntifraud, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '发布/转发动态反诈', - subtitle: '发布/转发动态后检查动态是否可见', - leading: const Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Icon(Icons.shield_outlined), - Icon(Icons.motion_photos_on, size: 12), - ], - ), - setKey: SettingBoxKey.enableCreateDynAntifraud, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '屏蔽带货动态', - leading: const Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Icon(Icons.shopping_bag_outlined, size: 14), - Icon(Icons.not_interested), - ], - ), - setKey: SettingBoxKey.antiGoodsDyn, - defaultVal: false, - onChanged: (value) { - GStorage.antiGoodsDyn = value; - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '屏蔽带货评论', - leading: const Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Icon(Icons.shopping_bag_outlined, size: 14), - Icon(Icons.not_interested), - ], - ), - setKey: SettingBoxKey.antiGoodsReply, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '侧滑关闭二级评论页面', - leading: Transform.rotate( - angle: pi * 1.5, - child: const Icon(Icons.touch_app), - ), - setKey: SettingBoxKey.slideDismissReplyPage, - defaultVal: Platform.isIOS, - onChanged: (value) { - GStorage.slideDismissReplyPage = value; - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '全屏展示点赞/投币/收藏等操作按钮', - leading: const Icon(MdiIcons.dotsHorizontalCircleOutline), - setKey: SettingBoxKey.showFSActionItem, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '启用双指缩小视频', - leading: const Icon(Icons.pinch), - setKey: SettingBoxKey.enableShrinkVideoSize, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '动态/专栏详情页展示底部操作栏', - leading: const Icon(Icons.more_horiz), - setKey: SettingBoxKey.showDynActionBar, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '启用拖拽字幕调整底部边距', - leading: const Icon(MdiIcons.dragVariant), - setKey: SettingBoxKey.enableDragSubtitle, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '展示追番时间表', - leading: const Icon(MdiIcons.chartTimelineVariantShimmer), - setKey: SettingBoxKey.showPgcTimeline, - defaultVal: true, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - setKey: SettingBoxKey.feedBackEnable, - onChanged: (value) { - enableFeedback = value; - feedBack(); - }, - leading: const Icon(Icons.vibration_outlined), - title: '震动反馈', - subtitle: '请确定手机设置中已开启震动反馈', - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '大家都在搜', - subtitle: '是否展示「大家都在搜」', - leading: const Icon(Icons.data_thresholding_outlined), - setKey: SettingBoxKey.enableHotKey, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '搜索发现', - subtitle: '是否展示「搜索发现」', - leading: const Icon(Icons.search_outlined), - setKey: SettingBoxKey.enableSearchRcmd, - defaultVal: true, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '搜索默认词', - subtitle: '是否展示搜索框默认词', - leading: const Icon(Icons.whatshot_outlined), - setKey: SettingBoxKey.enableSearchWord, - defaultVal: true, - onChanged: (val) { - Get.find().defaultSearch.value = ''; - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '快速收藏', - subtitle: '点按收藏至默认,长按选择文件夹', - leading: const Icon(Icons.bookmark_add_outlined), - setKey: SettingBoxKey.enableQuickFav, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '评论区搜索关键词', - subtitle: '展示评论区搜索关键词', - leading: const Icon(Icons.search_outlined), - setKey: SettingBoxKey.enableWordRe, - defaultVal: false, - onChanged: (value) { - ReplyItemGrpc.enableWordRe = value; - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '启用AI总结', - subtitle: '视频详情页开启AI总结', - leading: const Icon(Icons.engineering_outlined), - setKey: SettingBoxKey.enableAi, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '消息页禁用"收到的赞"功能', - subtitle: '禁止打开入口,降低网络社交依赖', - leading: const Icon(Icons.beach_access_outlined), - setKey: SettingBoxKey.disableLikeMsg, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '默认展示评论区', - subtitle: '在视频详情页默认切换至评论区页(仅Tab型布局)', - leading: const Icon(Icons.mode_comment_outlined), - setKey: SettingBoxKey.defaultShowComment, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '启用HTTP/2', - leading: const Icon(Icons.swap_horizontal_circle_outlined), - setKey: SettingBoxKey.enableHttp2, - defaultVal: false, - needReboot: true, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '连接重试次数', - subtitle: '为0时禁用', - leading: const Icon(Icons.repeat), - onTap: (setState) async { - final result = await showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: '连接重试次数', - min: 0, - max: 8, - divisions: 8, - precise: 0, - value: GStorage.retryCount.toDouble(), - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.retryCount, result.toInt()); - setState(); - SmartDialog.showToast('重启生效'); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '连接重试间隔', - subtitle: '实际间隔 = 间隔 * 第x次重试', - leading: const Icon(Icons.more_time_outlined), - onTap: (setState) async { - final result = await showDialog( - context: Get.context!, - builder: (context) { - return SlideDialog( - title: '连接重试间隔', - min: 0, - max: 1000, - divisions: 10, - precise: 0, - value: GStorage.retryDelay.toDouble(), - suffix: 'ms', - ); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.retryDelay, result.toInt()); - setState(); - SmartDialog.showToast('重启生效'); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '评论展示', - setKey: SettingBoxKey.replySortType, - leading: const Icon(Icons.whatshot_outlined), - getSubtitle: () => - '当前优先展示「${ReplySortType.values[GStorage.defaultReplySort].title}」', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '评论展示', - value: GStorage.defaultReplySort, - values: ReplySortType.values.map((e) { - return (e.index, e.title); - }).toList(), - ); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.replySortType, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '动态展示', - setKey: SettingBoxKey.defaultDynamicType, - leading: const Icon(Icons.dynamic_feed_outlined), - getSubtitle: () => - '当前优先展示「${DynamicsTabType.values[GStorage.defaultDynamicType].labels}」', - onTap: (setState) async { - int? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '动态展示', - value: GStorage.defaultDynamicType, - values: DynamicsTabType.values.sublist(0, 4).map((e) { - return (e.index, e.labels); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting - .put(SettingBoxKey.defaultDynamicType, result); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '用户页默认展示TAB', - setKey: SettingBoxKey.memberTab, - leading: const Icon(Icons.tab), - getSubtitle: () => '当前优先展示「${GStorage.memberTab.title}」', - onTap: (setState) async { - MemberTabType? result = await showDialog( - context: Get.context!, - builder: (context) { - return SelectDialog( - title: '用户页默认展示TAB', - value: GStorage.memberTab, - values: MemberTabType.values.map((e) { - return (e, e.title); - }).toList()); - }, - ); - if (result != null) { - await GStorage.setting.put(SettingBoxKey.memberTab, result.index); - setState(); - } - }, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - onTap: () { - String systemProxyHost = GStorage.defaultSystemProxyHost; - String systemProxyPort = GStorage.defaultSystemProxyPort; - - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('设置代理'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 6), - TextField( - decoration: InputDecoration( - isDense: true, - labelText: systemProxyHost != '' - ? systemProxyHost - : '请输入Host,使用 . 分割', - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(6)), - ), - hintText: systemProxyHost, - ), - onChanged: (e) { - systemProxyHost = e; - }, - ), - const SizedBox(height: 10), - TextField( - keyboardType: TextInputType.number, - decoration: InputDecoration( - isDense: true, - labelText: - systemProxyPort != '' ? systemProxyPort : '请输入Port', - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(6)), - ), - hintText: systemProxyPort, - ), - onChanged: (e) { - systemProxyPort = e; - }, - ), - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () { - Get.back(); - GStorage.setting - .put(SettingBoxKey.systemProxyHost, systemProxyHost); - GStorage.setting - .put(SettingBoxKey.systemProxyPort, systemProxyPort); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - leading: const Icon(Icons.airplane_ticket_outlined), - title: '设置代理', - subtitle: '设置代理 host:port', - setKey: SettingBoxKey.enableSystemProxy, - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '自动清除缓存', - subtitle: '每次启动时清除缓存', - leading: const Icon(Icons.auto_delete_outlined), - setKey: SettingBoxKey.autoClearCache, - defaultVal: false, - ), - SettingsModel( - settingsType: SettingsType.normal, - title: '最大缓存大小', - getSubtitle: () { - final num = GStorage.maxCacheSize; - return '当前最大缓存大小: 「${num == 0 ? '无限' : CacheManage.formatSize(GStorage.maxCacheSize)}」'; - }, - onTap: (setState) { - showDialog( - context: Get.context!, - builder: (context) { - String valueStr = ''; - return AlertDialog( - title: const Text('最大缓存大小'), - content: TextField( - autofocus: true, - onChanged: (value) => valueStr = value, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], - decoration: const InputDecoration(suffixText: 'MB'), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - Get.back(); - num value = num.tryParse(valueStr) ?? 0; - await GStorage.setting - .put(SettingBoxKey.maxCacheSize, value * 1024 * 1024); - setState(); - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - }, - leading: const Icon(Icons.delete_outlined), - ), - SettingsModel( - settingsType: SettingsType.sw1tch, - title: '检查更新', - subtitle: '每次启动时检查是否需要更新', - leading: const Icon(Icons.system_update_alt_outlined), - setKey: SettingBoxKey.autoUpdate, - defaultVal: true, - onChanged: (val) { - if (val) { - Update.checkUpdate(false); - } - }, - ), - ]; - -SettingsModel _getBanwordModel( - {required BuildContext context, - required String title, - required String key, - required ValueChanged callback}) { - String banWord = GStorage.setting.get(key, defaultValue: ''); - return SettingsModel( - settingsType: SettingsType.normal, - leading: const Icon(Icons.filter_alt_outlined), - title: title, - getSubtitle: () => banWord.isEmpty ? "点击添加" : banWord, - onTap: (setState) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(title), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('使用|隔开,如:尝试|测试'), - TextFormField( - autofocus: true, - initialValue: banWord, - textInputAction: TextInputAction.newline, - minLines: 1, - maxLines: 4, - onChanged: (value) => banWord = value, - ) - ], - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: - TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - child: const Text('保存'), - onPressed: () async { - Get.back(); - await GStorage.setting.put(key, banWord); - setState(); - callback(RegExp(banWord, caseSensitive: false)); - SmartDialog.showToast('已保存'); - }, - ), - ], - ); - }, - ); - }, - ); -} - -SettingsModel _getVideoFilterSelectModel({ - required BuildContext context, - required String title, - String? subtitle, - String? suffix, - required String key, - required List values, - int defaultValue = 0, - bool isFilter = true, -}) { - int value = GStorage.setting.get(key, defaultValue: defaultValue); - return SettingsModel( - settingsType: SettingsType.normal, - title: '$title${isFilter ? '过滤' : ''}', - leading: const Icon(Icons.timelapse_outlined), - subtitle: subtitle, - getSubtitle: subtitle == null - ? () => isFilter - ? '过滤掉$title小于「$value${suffix ?? ""}」的视频' - : '当前$title:「$value${suffix ?? ""}」' - : null, - onTap: (setState) async { - var result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '选择$title${isFilter ? '(0即不过滤)' : ''}', - value: value, - values: (values - ..addIf(!values.contains(value), value) - ..sort()) - .map((e) => (e, suffix == null ? e.toString() : '$e $suffix')) - .toList() - ..add((-1, '自定义'))); - }, - ); - if (result != null) { - if (result == -1 && context.mounted) { - await showDialog( - context: context, - builder: (context) { - String valueStr = ''; - return AlertDialog( - title: Text('自定义$title'), - content: TextField( - autofocus: true, - onChanged: (value) => valueStr = value, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'\d+')), - ], - decoration: InputDecoration(suffixText: suffix), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () { - Get.back(); - result = int.tryParse(valueStr) ?? 0; - }, - child: const Text('确定'), - ), - ], - ); - }, - ); - } - if (result != -1) { - value = result!; - await GStorage.setting.put(key, result); - setState(); - if (isFilter) RecommendFilter.update(); - } - } - }, - ); -} diff --git a/lib/pages/setting/widgets/select_dialog.dart b/lib/pages/setting/widgets/select_dialog.dart index ac4f4c23b..a4c419f47 100644 --- a/lib/pages/setting/widgets/select_dialog.dart +++ b/lib/pages/setting/widgets/select_dialog.dart @@ -4,7 +4,7 @@ import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/common/video/cdn_type.dart'; import 'package:PiliPlus/models/video/play/url.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/video_utils.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -68,11 +68,11 @@ class CdnSelectDialog extends StatefulWidget { class _CdnSelectDialogState extends State { late final List> _cdnResList; late final CancelToken _cancelToken; - bool _cdnSpeedTest = false; + late final bool _cdnSpeedTest; @override void initState() { - _cdnSpeedTest = GStorage.cdnSpeedTest; + _cdnSpeedTest = Pref.cdnSpeedTest; if (_cdnSpeedTest) { _cdnResList = List.generate( CDNService.values.length, (_) => ValueNotifier(null)); @@ -181,8 +181,8 @@ class _CdnSelectDialogState extends State { Widget build(BuildContext context) { return SelectDialog( title: 'CDN 设置', - values: CDNService.values.map((i) => (i.code, i.description)).toList(), - value: GStorage.defaultCDNService, + values: CDNService.values.map((i) => (i.code, i.desc)).toList(), + value: VideoUtils.cdnService, subtitleBuilder: _cdnSpeedTest ? (context, index) => ValueListenableBuilder( valueListenable: _cdnResList[index], diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index c60f3a9ed..898074d77 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -1,4 +1,6 @@ import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -38,7 +40,7 @@ class _SetSwitchItemState extends State { void setVal() { if (widget.setKey == SettingBoxKey.appFontWeight) { - val = GStorage.appFontWeight != -1; + val = Pref.appFontWeight != -1; } else { val = GStorage.setting .get(widget.setKey, defaultValue: widget.defaultVal ?? false); diff --git a/lib/pages/settings_search/view.dart b/lib/pages/settings_search/view.dart index 4e6faed2c..948661b96 100644 --- a/lib/pages/settings_search/view.dart +++ b/lib/pages/settings_search/view.dart @@ -1,6 +1,11 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; -import 'package:PiliPlus/models/common/settings_type.dart'; -import 'package:PiliPlus/pages/setting/widgets/model.dart'; +import 'package:PiliPlus/pages/setting/models/extra_settings.dart'; +import 'package:PiliPlus/pages/setting/models/model.dart'; +import 'package:PiliPlus/pages/setting/models/play_settings.dart'; +import 'package:PiliPlus/pages/setting/models/privacy_settings.dart'; +import 'package:PiliPlus/pages/setting/models/recommend_settings.dart'; +import 'package:PiliPlus/pages/setting/models/style_settings.dart'; +import 'package:PiliPlus/pages/setting/models/video_settings.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; @@ -24,7 +29,7 @@ class _SettingsSearchPageState extends State { ...videoSettings, ...playSettings, ...styleSettings, - ]..removeWhere((item) => item.settingsType == SettingsType.divider); + ]; @override void dispose() { diff --git a/lib/pages/sponsor_block/view.dart b/lib/pages/sponsor_block/view.dart index 2bcc6a091..7c83ad68f 100644 --- a/lib/pages/sponsor_block/view.dart +++ b/lib/pages/sponsor_block/view.dart @@ -8,6 +8,8 @@ import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart'; import 'package:PiliPlus/pages/setting/slide_color_picker.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show FilteringTextInputFormatter; @@ -26,27 +28,20 @@ class SponsorBlockPage extends StatefulWidget { class _SponsorBlockPageState extends State { final _url = 'https://github.com/hanydd/BilibiliSponsorBlock'; final _textController = TextEditingController(); - late double _blockLimit; - late List> _blockSettings; - late List _blockColor; - late String _userId; - late bool _blockToast; - late String _blockServer; - late bool _blockTrack; - bool? _serverStatus; + double _blockLimit = Pref.blockLimit; + final List> _blockSettings = Pref.blockSettings; + final List _blockColor = Pref.blockColor; + String _userId = Pref.blockUserID; + bool _blockToast = Pref.blockToast; + String _blockServer = Pref.blockServer; + bool _blockTrack = Pref.blockTrack; + final Rx _serverStatus = Rx(null); - Box get setting => GStorage.setting; + Box setting = GStorage.setting; @override void initState() { super.initState(); - _blockLimit = GStorage.blockLimit; - _blockSettings = GStorage.blockSettings; - _blockColor = GStorage.blockColor; - _userId = GStorage.blockUserID; - _blockToast = GStorage.blockToast; - _blockServer = GStorage.blockServer; - _blockTrack = GStorage.blockTrack; _checkServerStatus(); } @@ -57,18 +52,10 @@ class _SponsorBlockPageState extends State { } void _checkServerStatus() { - Request() - .get( - '$_blockServer/api/status/uptime', - ) - .then((res) { - if (mounted) { - setState(() { - _serverStatus = res.statusCode == 200 && - res.data is String && - Utils.isStringNumeric(res.data); - }); - } + Request().get('$_blockServer/api/status/uptime').then((res) { + _serverStatus.value = res.statusCode == 200 && + res.data is String && + Utils.isStringNumeric(res.data); }); } @@ -352,30 +339,30 @@ class _SponsorBlockPageState extends State { }, ); - Widget _serverStatusItem(ThemeData theme, TextStyle titleStyle) => Builder( - builder: (context) { + Widget _serverStatusItem(ThemeData theme, TextStyle titleStyle) => Obx( + () { + String status; + Color? color; + switch (_serverStatus.value) { + case null: + status = '——'; + case true: + status = '正常'; + color = theme.colorScheme.primary; + case false: + status = '错误'; + color = theme.colorScheme.error; + } return ListTile( dense: true, onTap: () { - _serverStatus = null; - (context as Element).markNeedsBuild(); + _serverStatus.value = null; _checkServerStatus(); }, title: Text('服务器状态', style: titleStyle), trailing: Text( - _serverStatus == null - ? '——' - : _serverStatus == true - ? '正常' - : '错误', - style: TextStyle( - fontSize: 13, - color: _serverStatus == null - ? null - : _serverStatus == true - ? theme.colorScheme.primary - : theme.colorScheme.error, - ), + status, + style: TextStyle(fontSize: 13, color: color), ), ); }, diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index f1097d954..be8c5f7a8 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -43,6 +43,7 @@ import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/video_utils.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; @@ -105,7 +106,7 @@ class VideoDetailController extends GetxController late final headerCtrKey = GlobalKey(); - Box get setting => GStorage.setting; + Box setting = GStorage.setting; late String cacheDecode; late String cacheSecondDecode; @@ -277,20 +278,16 @@ class VideoDetailController extends GetxController getMediaList(); } - bool defaultShowComment = - setting.get(SettingBoxKey.defaultShowComment, defaultValue: false); + bool defaultShowComment = Pref.defaultShowComment; tabCtr = TabController( length: 2, vsync: this, initialIndex: defaultShowComment ? 1 : 0); - autoPlay.value = - setting.get(SettingBoxKey.autoPlayEnable, defaultValue: false); + autoPlay.value = Pref.autoPlayEnable; if (autoPlay.value) isShowCover.value = false; danmakuCid.value = cid.value; // 预设的解码格式 - cacheDecode = setting.get(SettingBoxKey.defaultDecode, - defaultValue: VideoDecodeFormatType.values.last.code); - cacheSecondDecode = setting.get(SettingBoxKey.secondDecode, - defaultValue: VideoDecodeFormatType.values[1].code); + cacheDecode = Pref.defaultDecode; + cacheSecondDecode = Pref.secondDecode; oid.value = IdUtils.bv2av(Get.parameters['bvid']!); } @@ -457,7 +454,7 @@ class VideoDetailController extends GetxController '$blockServer/api/voteOnSponsorTime', queryParameters: { 'UUID': uuid, - 'userID': GStorage.blockUserID, + 'userID': Pref.blockUserID, 'type': type, }, ).then((res) { @@ -483,7 +480,7 @@ class VideoDetailController extends GetxController '$blockServer/api/voteOnSponsorTime', queryParameters: { 'UUID': segment.UUID, - 'userID': GStorage.blockUserID, + 'userID': Pref.blockUserID, 'category': item.name, }, ).then((res) { @@ -905,10 +902,10 @@ class VideoDetailController extends GetxController await plPlayerController.videoPlayerController ?.seek(Duration(milliseconds: item.segment.second)); if (isSkip) { - if (GStorage.blockToast) { + if (Pref.blockToast) { _showBlockToast('已跳过${item.segmentType.shortTitle}片段'); } - if (GStorage.blockTrack) { + if (Pref.blockTrack) { Request().post( '$blockServer/api/viewedVideoSponsorTime', queryParameters: {'UUID': item.UUID}, @@ -1135,15 +1132,11 @@ class VideoDetailController extends GetxController await Connectivity().checkConnectivity().then((res) { plPlayerController ..cacheVideoQa = res.contains(ConnectivityResult.wifi) - ? setting.get(SettingBoxKey.defaultVideoQa, - defaultValue: VideoQuality.values.last.code) - : setting.get(SettingBoxKey.defaultVideoQaCellular, - defaultValue: VideoQuality.high1080.code) + ? Pref.defaultVideoQa + : Pref.defaultVideoQaCellular ..cacheAudioQa = res.contains(ConnectivityResult.wifi) - ? setting.get(SettingBoxKey.defaultAudioQa, - defaultValue: AudioQuality.hiRes.code) - : setting.get(SettingBoxKey.defaultAudioQaCellular, - defaultValue: AudioQuality.k192.code); + ? Pref.defaultAudioQa + : Pref.defaultAudioQaCellular; }); } var result = await VideoHttp.videoUrl( @@ -1436,7 +1429,7 @@ class VideoDetailController extends GetxController } } - late bool continuePlayingPart = GStorage.continuePlayingPart; + late bool continuePlayingPart = Pref.continuePlayingPart; Future _queryPlayInfo() async { var res = await VideoHttp.playInfo(bvid: bvid, cid: cid.value); @@ -1474,7 +1467,7 @@ class VideoDetailController extends GetxController } catch (_) {} } - if (playInfo.viewPoints?.isNotEmpty == true && GStorage.showViewPoints) { + if (playInfo.viewPoints?.isNotEmpty == true && Pref.showViewPoints) { try { viewPointList = playInfo.viewPoints!.map((item) { double start = @@ -1500,7 +1493,7 @@ class VideoDetailController extends GetxController int idx = 0; subtitles.value = playInfo.subtitle!.subtitles!; - String preference = GStorage.defaultSubtitlePreference; + String preference = Pref.subtitlePreference; if (preference != 'off') { idx = subtitles.indexWhere((i) => !i.lan!.startsWith('ai')) + 1; if (idx == 0) { diff --git a/lib/pages/video/introduction/ugc/controller.dart b/lib/pages/video/introduction/ugc/controller.dart index ca1feecf8..79fe7eda0 100644 --- a/lib/pages/video/introduction/ugc/controller.dart +++ b/lib/pages/video/introduction/ugc/controller.dart @@ -33,7 +33,7 @@ import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -64,7 +64,7 @@ class VideoIntroController extends CommonIntroController { RxInt lastPlayCid = 0.obs; // 同时观看 - bool isShowOnlineTotal = false; + final bool isShowOnlineTotal = Pref.enableOnlineTotal; late final RxString total = '1'.obs; Timer? timer; String heroTag = ''; @@ -74,9 +74,8 @@ class VideoIntroController extends CommonIntroController { ExpandableController? expandableCtr; - late final showArgueMsg = GStorage.showArgueMsg; - late final enableAi = - GStorage.setting.get(SettingBoxKey.enableAi, defaultValue: false); + late final showArgueMsg = Pref.showArgueMsg; + late final enableAi = Pref.enableAi; @override void onInit() { @@ -113,8 +112,6 @@ class VideoIntroController extends CommonIntroController { } } lastPlayCid.value = int.parse(Get.parameters['cid']!); - isShowOnlineTotal = GStorage.setting - .get(SettingBoxKey.enableOnlineTotal, defaultValue: false); startTimer(); queryVideoIntro(); } @@ -560,7 +557,7 @@ class VideoIntroController extends CommonIntroController { reSrc: 11, ); if (res['status']) { - GStorage.removeBlackMid(mid); + Pref.removeBlackMid(mid); followStatus['attribute'] = 0; } return; diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index 6f80971d4..a1a36650d 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -23,7 +23,7 @@ import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/gestures.dart'; @@ -128,7 +128,7 @@ class _VideoInfoState extends State { bool isProcessing = false; - late final _horizontalMemberPage = GStorage.horizontalMemberPage; + late final _horizontalMemberPage = Pref.horizontalMemberPage; Widget _buildVideoTitle(ThemeData theme, [bool isExpand = false]) => videoDetailCtr.plPlayerController.enableSponsorBlock @@ -219,13 +219,13 @@ class _VideoInfoState extends State { videoDetailCtr = Get.find(tag: widget.heroTag); if (videoIntroController.expandableCtr == null) { - bool alwaysExapndIntroPanel = GStorage.alwaysExapndIntroPanel; + bool alwaysExapndIntroPanel = Pref.alwaysExapndIntroPanel; videoIntroController.expandableCtr = ExpandableController( initialExpanded: alwaysExapndIntroPanel, ); - if (!alwaysExapndIntroPanel && GStorage.exapndIntroPanelH) { + if (!alwaysExapndIntroPanel && Pref.exapndIntroPanelH) { WidgetsBinding.instance.addPostFrameCallback((_) { if (context.orientation == Orientation.landscape && videoIntroController.expandableCtr?.expanded == false) { diff --git a/lib/pages/video/note/view.dart b/lib/pages/video/note/view.dart index 29b95228c..5f20985dd 100644 --- a/lib/pages/video/note/view.dart +++ b/lib/pages/video/note/view.dart @@ -9,8 +9,8 @@ import 'package:PiliPlus/models_new/video/video_note_list/list.dart'; import 'package:PiliPlus/pages/common/common_slide_page.dart'; import 'package:PiliPlus/pages/video/note/controller.dart'; import 'package:PiliPlus/pages/webview/view.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/video/pay_coins/view.dart b/lib/pages/video/pay_coins/view.dart index f7cf9723a..31f5cee1a 100644 --- a/lib/pages/video/pay_coins/view.dart +++ b/lib/pages/video/pay_coins/view.dart @@ -3,6 +3,8 @@ import 'dart:math'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get/get_navigation/src/dialog/dialog_route.dart'; @@ -59,7 +61,7 @@ class _PayCoinsPageState extends State with TickerProviderStateMixin { bool _isPaying = false; late final _controller = PageController(viewportFraction: 0.30); - late final RxBool _coinWithLike = GStorage.coinWithLike.obs; + late final RxBool _coinWithLike = Pref.coinWithLike.obs; final _key = GlobalKey(); int get _index => _controller.hasClients ? _controller.page?.round() ?? 0 : 0; diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index 62bc5ac23..f00403c0c 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -14,7 +14,7 @@ import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/utils/duration_util.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -575,7 +575,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { data: { 'videoID': videoDetailController.bvid, 'cid': videoDetailController.cid.value.toString(), - 'userID': GStorage.blockUserID.toString(), + 'userID': Pref.blockUserID.toString(), 'userAgent': Constants.userAgent, 'videoDuration': videoDuration, 'segments': list! diff --git a/lib/pages/video/reply/controller.dart b/lib/pages/video/reply/controller.dart index 29d0181f0..ab2158f18 100644 --- a/lib/pages/video/reply/controller.dart +++ b/lib/pages/video/reply/controller.dart @@ -54,7 +54,6 @@ class VideoReplyController extends ReplyController mode: mode.value, cursorNext: cursorNext, offset: paginationReply?.nextOffset, - antiGoodsReply: antiGoodsReply, ); @override diff --git a/lib/pages/video/reply/view.dart b/lib/pages/video/reply/view.dart index 989358819..54529ae57 100644 --- a/lib/pages/video/reply/view.dart +++ b/lib/pages/video/reply/view.dart @@ -5,7 +5,6 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; import 'package:PiliPlus/pages/video/reply/controller.dart'; import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; import 'package:PiliPlus/utils/feed_back.dart'; diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index 0e3b34d2a..805479d0a 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -16,14 +16,14 @@ import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/pages/save_panel/view.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/pages/video/reply/widgets/zan_grpc.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; -import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/url_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -70,8 +70,8 @@ class ReplyItemGrpc extends StatelessWidget { static final _voteRegExp = RegExp(r"\{vote:\d+?\}"); static final _timeRegExp = RegExp(r'^\b(?:\d+[::])?\d+[::]\d+\b$'); - static bool enableWordRe = - GStorage.setting.get(SettingBoxKey.enableWordRe, defaultValue: false); + static bool enableWordRe = Pref.enableWordRe; + static int replyLengthLimit = Pref.replyLengthLimit; @override Widget build(BuildContext context) { @@ -272,10 +272,10 @@ class ReplyItemGrpc extends StatelessWidget { ); TextPainter? textPainter; bool? didExceedMaxLines; - if (replyLevel == 1 && GlobalData().replyLengthLimit != 0) { + if (replyLevel == 1 && replyLengthLimit != 0) { textPainter = TextPainter( text: TextSpan(text: text, style: style), - maxLines: GlobalData().replyLengthLimit, + maxLines: replyLengthLimit, textDirection: Directionality.of(context), )..layout(maxWidth: constraints.maxWidth); didExceedMaxLines = textPainter.didExceedMaxLines; diff --git a/lib/pages/video/reply_new/view.dart b/lib/pages/video/reply_new/view.dart index fe0ae4747..96e57be8d 100644 --- a/lib/pages/video/reply_new/view.dart +++ b/lib/pages/video/reply_new/view.dart @@ -8,7 +8,7 @@ import 'package:PiliPlus/main.dart'; import 'package:PiliPlus/models/common/publish_panel_type.dart'; import 'package:PiliPlus/pages/common/common_publish_page.dart'; import 'package:PiliPlus/pages/emote/view.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -75,7 +75,7 @@ class _ReplyPageState extends CommonPublishPageState { } late final darkVideoPage = - Get.currentRoute.startsWith('/video') && GStorage.darkVideoPage; + Get.currentRoute.startsWith('/video') && Pref.darkVideoPage; late ThemeData themeData; @override diff --git a/lib/pages/video/reply_reply/controller.dart b/lib/pages/video/reply_reply/controller.dart index 9e3cf2378..e2d3c8f15 100644 --- a/lib/pages/video/reply_reply/controller.dart +++ b/lib/pages/video/reply_reply/controller.dart @@ -4,7 +4,7 @@ import 'package:PiliPlus/grpc/reply.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/pages/common/reply_controller.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; @@ -36,7 +36,7 @@ class VideoReplyReplyController extends ReplyController int? index; AnimationController? controller; - late final horizontalPreview = GStorage.horizontalPreview; + late final horizontalPreview = Pref.horizontalPreview; @override dynamic get sourceId => replyType == 1 ? IdUtils.av2bv(oid) : oid; @@ -111,7 +111,6 @@ class VideoReplyReplyController extends ReplyController root: rpid, dialog: dialog!, offset: paginationReply?.nextOffset, - antiGoodsReply: antiGoodsReply, ) : ReplyGrpc.detailList( type: replyType, @@ -120,7 +119,6 @@ class VideoReplyReplyController extends ReplyController rpid: id ?? 0, mode: mode.value, offset: paginationReply?.nextOffset, - antiGoodsReply: antiGoodsReply, ); @override diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index fab7c4679..e5986606d 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -43,12 +43,14 @@ import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/plugin/pl_player/view.dart'; import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/services/shutdown_timer_service.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:auto_orientation/auto_orientation.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; @@ -61,7 +63,6 @@ import 'package:flutter/services.dart' show SystemUiOverlayStyle; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:screen_brightness/screen_brightness.dart'; class VideoDetailPageV extends StatefulWidget { @@ -83,12 +84,17 @@ class _VideoDetailPageVState extends State late final _introController = ScrollController(); late String heroTag; - // 自动退出全屏 - late bool autoExitFullscreen; - late bool autoPlayEnable; - late bool enableVerticalExpand; - late bool pipNoDanmaku; - late bool removeSafeArea; + bool get autoExitFullscreen => + videoDetailController.plPlayerController.autoExitFullscreen; + bool get autoPlayEnable => + videoDetailController.plPlayerController.autoPlayEnable; + bool get enableVerticalExpand => + videoDetailController.plPlayerController.enableVerticalExpand; + bool get pipNoDanmaku => + videoDetailController.plPlayerController.pipNoDanmaku; + bool get removeSafeArea => + videoDetailController.plPlayerController.removeSafeArea; + bool isShowing = true; bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false; @@ -104,8 +110,6 @@ class _VideoDetailPageVState extends State StreamSubscription? _listenerFS; - Box get setting => GStorage.setting; - final GlobalKey relatedVideoPanelKey = GlobalKey(); final GlobalKey videoPlayerKey = GlobalKey(); final GlobalKey videoReplyPanelKey = GlobalKey(); @@ -132,15 +136,7 @@ class _VideoDetailPageVState extends State if (videoDetailController.videoType == SearchType.media_bangumi) { pgcIntroController = Get.put(PgcIntroController(), tag: heroTag); } - autoExitFullscreen = - setting.get(SettingBoxKey.enableAutoExit, defaultValue: true); - autoPlayEnable = - setting.get(SettingBoxKey.autoPlayEnable, defaultValue: false); - pipNoDanmaku = setting.get(SettingBoxKey.pipNoDanmaku, defaultValue: false); - enableVerticalExpand = - setting.get(SettingBoxKey.enableVerticalExpand, defaultValue: false); - removeSafeArea = setting.get(SettingBoxKey.videoPlayerRemoveSafeArea, - defaultValue: false); + if (removeSafeArea) hideStatusBar(); videoSourceInit(); autoScreen(); @@ -1532,22 +1528,22 @@ class _VideoDetailPageVState extends State () => IconButton( onPressed: () { videoDetailController - .plPlayerController.isOpenDanmu.value = + .plPlayerController.enableShowDanmaku.value = !videoDetailController - .plPlayerController.isOpenDanmu.value; - setting.put( + .plPlayerController.enableShowDanmaku.value; + GStorage.setting.put( SettingBoxKey.enableShowDanmaku, videoDetailController - .plPlayerController.isOpenDanmu.value); + .plPlayerController.enableShowDanmaku.value); }, icon: Icon( size: 22, videoDetailController - .plPlayerController.isOpenDanmu.value + .plPlayerController.enableShowDanmaku.value ? CustomIcon.dm_on : CustomIcon.dm_off, color: videoDetailController - .plPlayerController.isOpenDanmu.value + .plPlayerController.enableShowDanmaku.value ? themeData.colorScheme.secondary : themeData.colorScheme.outline, ), diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index eee93a406..cf9d9a4d1 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -21,11 +21,16 @@ import 'package:PiliPlus/pages/video/introduction/ugc/widgets/menu_row.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; +import 'package:PiliPlus/services/service_locator.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/video_utils.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dio/dio.dart'; @@ -65,12 +70,11 @@ class HeaderControlState extends State { String get heroTag => widget.heroTag; late VideoIntroController videoIntroController; late PgcIntroController pgcIntroController; - late bool horizontalScreen; + bool get horizontalScreen => videoDetailCtr.horizontalScreen; RxString now = ''.obs; Timer? clock; - late String defaultCDNService; bool get isFullScreen => widget.controller.isFullScreen.value; - Box get setting => GStorage.setting; + Box setting = GStorage.setting; late final _coinKey = GlobalKey(); late final _favKey = GlobalKey(); @@ -84,9 +88,6 @@ class HeaderControlState extends State { if (videoDetailCtr.videoType != SearchType.video) { pgcIntroController = Get.find(tag: heroTag); } - horizontalScreen = GStorage.horizontalScreen; - defaultCDNService = setting.get(SettingBoxKey.CDNService, - defaultValue: CDNService.backupUrl.code); } @override @@ -245,7 +246,7 @@ class HeaderControlState extends State { title: const Text('CDN 设置', style: titleStyle), leading: const Icon(MdiIcons.cloudPlusOutline, size: 20), subtitle: Text( - '当前:${CDNService.fromCode(defaultCDNService).description},无法播放请切换', + '当前:${CDNService.fromCode(VideoUtils.cdnService).desc},无法播放请切换', style: subTitleStyle, ), onTap: () async { @@ -258,10 +259,10 @@ class HeaderControlState extends State { }, ); if (result != null) { - defaultCDNService = result; + VideoUtils.cdnService = result; setting.put(SettingBoxKey.CDNService, result); SmartDialog.showToast( - '已设置为 ${CDNService.fromCode(result).description},正在重载视频'); + '已设置为 ${CDNService.fromCode(result).desc},正在重载视频'); setState(() {}); videoDetailCtr.queryVideoUrl( videoDetailCtr.playedTime, @@ -333,8 +334,7 @@ class HeaderControlState extends State { onTap: () => {Get.back(), showSetVideoQa()}, leading: const Icon(Icons.play_circle_outline, size: 20), title: const Text('选择画质', style: titleStyle), - subtitle: Text( - '当前画质 ${videoDetailCtr.currentVideoQa.description}', + subtitle: Text('当前画质 ${videoDetailCtr.currentVideoQa.desc}', style: subTitleStyle), ), if (videoDetailCtr.currentAudioQa != null) @@ -344,7 +344,7 @@ class HeaderControlState extends State { leading: const Icon(Icons.album_outlined, size: 20), title: const Text('选择音质', style: titleStyle), subtitle: Text( - '当前音质 ${videoDetailCtr.currentAudioQa!.description}', + '当前音质 ${videoDetailCtr.currentAudioQa!.desc}', style: subTitleStyle), ), ListTile( @@ -361,7 +361,7 @@ class HeaderControlState extends State { onTap: () => {Get.back(), showSetRepeat()}, leading: const Icon(Icons.repeat, size: 20), title: const Text('播放顺序', style: titleStyle), - subtitle: Text(widget.controller.playRepeat.description, + subtitle: Text(widget.controller.playRepeat.desc, style: subTitleStyle), ), ListTile( @@ -611,16 +611,15 @@ class HeaderControlState extends State { await Connectivity().checkConnectivity().then((res) { if (res.contains(ConnectivityResult.wifi)) { oldQualityDesc = - VideoQuality.fromCode(GStorage.defaultVideoQa) - .description; + VideoQuality.fromCode(Pref.defaultVideoQa).desc; setting.put( SettingBoxKey.defaultVideoQa, quality, ); } else { - oldQualityDesc = VideoQuality.fromCode( - GStorage.defaultVideoQaCellular) - .description; + oldQualityDesc = + VideoQuality.fromCode(Pref.defaultVideoQaCellular) + .desc; setting.put( SettingBoxKey.defaultVideoQaCellular, quality, @@ -628,7 +627,7 @@ class HeaderControlState extends State { } }); SmartDialog.showToast( - "默认画质由:$oldQualityDesc 变为:${VideoQuality.fromCode(quality).description}", + "默认画质由:$oldQualityDesc 变为:${VideoQuality.fromCode(quality).desc}", ); }, // 可能包含会员解锁画质 @@ -694,16 +693,15 @@ class HeaderControlState extends State { await Connectivity().checkConnectivity().then((res) { if (res.contains(ConnectivityResult.wifi)) { oldQualityDesc = - AudioQuality.fromCode(GStorage.defaultAudioQa) - .description; + AudioQuality.fromCode(Pref.defaultAudioQa).desc; setting.put( SettingBoxKey.defaultAudioQa, quality, ); } else { - oldQualityDesc = AudioQuality.fromCode( - GStorage.defaultAudioQaCellular) - .description; + oldQualityDesc = + AudioQuality.fromCode(Pref.defaultAudioQaCellular) + .desc; setting.put( SettingBoxKey.defaultAudioQaCellular, quality, @@ -711,7 +709,7 @@ class HeaderControlState extends State { } }); SmartDialog.showToast( - "默认音质由:$oldQualityDesc 变为:${AudioQuality.fromCode(quality).description}", + "默认音质由:$oldQualityDesc 变为:${AudioQuality.fromCode(quality).desc}", ); }, contentPadding: const EdgeInsets.only(left: 20, right: 20), @@ -1215,11 +1213,11 @@ class HeaderControlState extends State { // 显示区域 double showArea = widget.controller.showArea; // 不透明度 - double opacity = widget.controller.opacity; + double opacity = widget.controller.danmakuOpacity; // 字体大小 - double fontSize = widget.controller.fontSize; + double fontSize = widget.controller.danmakuFontScale; // 全屏字体大小 - double fontSizeFS = widget.controller.fontSizeFS; + double fontSizeFS = widget.controller.danmakuFontScaleFS; double danmakuLineHeight = widget.controller.danmakuLineHeight; // 弹幕速度 double danmakuDuration = widget.controller.danmakuDuration; @@ -1293,7 +1291,7 @@ class HeaderControlState extends State { void updateFontSizeFS(double val) { fontSizeFS = val; widget.controller - ..fontSizeFS = fontSizeFS + ..danmakuFontScaleFS = fontSizeFS ..putDanmakuSettings(); setState(() {}); if (widget.controller.isFullScreen.value == true) { @@ -1310,7 +1308,7 @@ class HeaderControlState extends State { void updateFontSize(double val) { fontSize = val; widget.controller - ..fontSize = fontSize + ..danmakuFontScale = fontSize ..putDanmakuSettings(); setState(() {}); if (widget.controller.isFullScreen.value == false) { @@ -1353,7 +1351,7 @@ class HeaderControlState extends State { void updateOpacity(double val) { opacity = val; widget.controller - ..opacity = opacity + ..danmakuOpacity = opacity ..putDanmakuSettings(); setState(() {}); try { @@ -1769,7 +1767,7 @@ class HeaderControlState extends State { Get.back(); }, contentPadding: const EdgeInsets.only(left: 20, right: 20), - title: Text(i.description), + title: Text(i.desc), trailing: widget.controller.playRepeat == i ? Icon( Icons.done, @@ -2045,18 +2043,18 @@ class HeaderControlState extends State { child: Obx( () => IconButton( tooltip: - "${plPlayerController.isOpenDanmu.value ? '关闭' : '开启'}弹幕", + "${plPlayerController.enableShowDanmaku.value ? '关闭' : '开启'}弹幕", style: ButtonStyle( padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { - plPlayerController.isOpenDanmu.value = - !plPlayerController.isOpenDanmu.value; + plPlayerController.enableShowDanmaku.value = + !plPlayerController.enableShowDanmaku.value; setting.put(SettingBoxKey.enableShowDanmaku, - plPlayerController.isOpenDanmu.value); + plPlayerController.enableShowDanmaku.value); }, icon: Icon( - plPlayerController.isOpenDanmu.value + plPlayerController.enableShowDanmaku.value ? Icons.subtitles_outlined : Icons.subtitles_off_outlined, size: 19, @@ -2078,10 +2076,8 @@ class HeaderControlState extends State { bool canUsePiP = await Floating().isPipAvailable; widget.controller.hiddenControls(false); if (canUsePiP) { - bool enableBackgroundPlay = setting.get( - SettingBoxKey.enableBackgroundPlay, - defaultValue: true); - if (!enableBackgroundPlay && mounted) { + if (!videoPlayerServiceHandler.enableBackgroundPlay && + mounted) { final theme = Theme.of(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( diff --git a/lib/pages/webdav/view.dart b/lib/pages/webdav/view.dart index 5eff31034..ec0784e94 100644 --- a/lib/pages/webdav/view.dart +++ b/lib/pages/webdav/view.dart @@ -1,6 +1,8 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/pages/webdav/webdav.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -17,10 +19,10 @@ class WebDavSettingPage extends StatefulWidget { } class _WebDavSettingPageState extends State { - final _uriCtr = TextEditingController(text: GStorage.webdavUri); - final _usernameCtr = TextEditingController(text: GStorage.webdavUsername); - final _passwordCtr = TextEditingController(text: GStorage.webdavPassword); - final _directoryCtr = TextEditingController(text: GStorage.webdavDirectory); + final _uriCtr = TextEditingController(text: Pref.webdavUri); + final _usernameCtr = TextEditingController(text: Pref.webdavUsername); + final _passwordCtr = TextEditingController(text: Pref.webdavPassword); + final _directoryCtr = TextEditingController(text: Pref.webdavDirectory); @override void dispose() { @@ -115,13 +117,11 @@ class _WebDavSettingPageState extends State { SmartDialog.showToast('地址不能为空'); return; } - await GStorage.setting.put(SettingBoxKey.webdavUri, _uriCtr.text); - await GStorage.setting - .put(SettingBoxKey.webdavUsername, _usernameCtr.text); - await GStorage.setting - .put(SettingBoxKey.webdavPassword, _passwordCtr.text); - await GStorage.setting - .put(SettingBoxKey.webdavDirectory, _directoryCtr.text); + final setting = GStorage.setting; + await setting.put(SettingBoxKey.webdavUri, _uriCtr.text); + await setting.put(SettingBoxKey.webdavUsername, _usernameCtr.text); + await setting.put(SettingBoxKey.webdavPassword, _passwordCtr.text); + await setting.put(SettingBoxKey.webdavDirectory, _directoryCtr.text); try { final res = await WebDav().init(); if (res.first) { diff --git a/lib/pages/webdav/webdav.dart b/lib/pages/webdav/webdav.dart index 60533a4df..59e71f2dc 100644 --- a/lib/pages/webdav/webdav.dart +++ b/lib/pages/webdav/webdav.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:PiliPlus/common/widgets/pair.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:webdav_client/webdav_client.dart' as webdav; @@ -17,10 +18,10 @@ class WebDav { factory WebDav() => _instance; Future> init() async { - final webDavUri = GStorage.webdavUri; - final webDavUsername = GStorage.webdavUsername; - final webDavPassword = GStorage.webdavPassword; - _webdavDirectory = GStorage.webdavDirectory; + final webDavUri = Pref.webdavUri; + final webDavUsername = Pref.webdavUsername; + final webDavPassword = Pref.webdavPassword; + _webdavDirectory = Pref.webdavDirectory; if (!_webdavDirectory.endsWith('/')) { _webdavDirectory += '/'; } diff --git a/lib/pages/webview/view.dart b/lib/pages/webview/view.dart index 3c08ab012..737e08b69 100644 --- a/lib/pages/webview/view.dart +++ b/lib/pages/webview/view.dart @@ -2,11 +2,11 @@ import 'dart:io'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/common/webview_menu_type.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/cache_manage.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index 16b677827..23ed9f645 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -4,7 +4,7 @@ import 'package:PiliPlus/grpc/im.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models_new/msg/msgfeed_unread.dart'; import 'package:PiliPlus/pages/common/common_whisper_controller.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -26,8 +26,6 @@ class WhisperController extends CommonWhisperController { @override void onInit() { super.onInit(); - final disableLikeMsg = - GStorage.setting.get(SettingBoxKey.disableLikeMsg, defaultValue: false); msgFeedTopItems = [ const ( name: "回复我的", @@ -45,7 +43,7 @@ class WhisperController extends CommonWhisperController { name: "收到的赞", icon: Icons.favorite_border_outlined, route: "/likeMe", - enabled: !disableLikeMsg, + enabled: !Pref.disableLikeMsg, ), const ( name: "系统通知", diff --git a/lib/pages/whisper_link_setting/controller.dart b/lib/pages/whisper_link_setting/controller.dart index f9d025414..3424cabaa 100644 --- a/lib/pages/whisper_link_setting/controller.dart +++ b/lib/pages/whisper_link_setting/controller.dart @@ -10,7 +10,8 @@ import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models_new/msg/im_user_infos/datum.dart'; import 'package:PiliPlus/models_new/msg/msg_dnd/uid_setting.dart'; import 'package:PiliPlus/models_new/msg/session_ss/data.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/accounts.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -127,7 +128,7 @@ class WhisperLinkSettingController extends GetxController { sessionSs ..value.data.followStatus = null ..refresh(); - GStorage.removeBlackMid(talkerUid); + Pref.removeBlackMid(talkerUid); } else { SmartDialog.showToast(res['msg']); } @@ -146,7 +147,7 @@ class WhisperLinkSettingController extends GetxController { sessionSs ..value.data.followStatus = 128 ..refresh(); - GStorage.setBlackMid(talkerUid); + Pref.setBlackMid(talkerUid); } else { SmartDialog.showToast(res['msg']); } diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 379ce9662..36af96b94 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -3,17 +3,16 @@ import 'dart:convert'; import 'dart:io'; import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/common/widgets/pair.dart'; import 'package:PiliPlus/common/widgets/progress_bar/segment_progress_bar.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/common/audio_normalization.dart'; -import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart'; import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart'; import 'package:PiliPlus/models/user/danmaku_rule.dart'; import 'package:PiliPlus/models_new/video/video_shot/data.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; +import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart'; import 'package:PiliPlus/plugin/pl_player/models/data_source.dart'; import 'package:PiliPlus/plugin/pl_player/models/data_status.dart'; import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; @@ -21,10 +20,13 @@ import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/services/service_locator.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/page_utils.dart' show PageUtils; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:crclib/catalog.dart'; @@ -78,8 +80,8 @@ class PlPlayerController { final RxInt _playerCount = 0.obs; late double lastPlaybackSpeed = 1.0; - final RxDouble _playbackSpeed = 1.0.obs; - final RxDouble _longPressSpeed = 2.0.obs; + final RxDouble _playbackSpeed = Pref.playSpeedDefault.obs; + late final RxDouble _longPressSpeed = Pref.longPressSpeedDefault.obs; final RxDouble _currentVolume = 1.0.obs; final RxDouble _currentBrightness = (-1.0).obs; @@ -91,7 +93,7 @@ class PlPlayerController { final RxBool _controlsLock = false.obs; final RxBool _isFullScreen = false.obs; // 默认投稿视频格式 - static RxString _videoType = 'archive'.obs; + static final RxString _videoType = 'archive'.obs; final RxString _direction = 'horizontal'.obs; @@ -100,7 +102,8 @@ class PlPlayerController { late StreamSubscription _dataListenerForEnterFullscreen; /// 后台播放 - late final RxBool _continuePlayInBackground = false.obs; + late final RxBool _continuePlayInBackground = + Pref.continuePlayInBackground.obs; late final RxBool _flipX = false.obs; @@ -135,7 +138,7 @@ class PlPlayerController { final RxBool showVP = true.obs; final RxList segmentList = [].obs; - Box get setting => GStorage.setting; + Box setting = GStorage.setting; // final Durations durations; @@ -246,10 +249,9 @@ class PlPlayerController { RxString get videoType => _videoType; /// 弹幕开关 - RxBool isOpenDanmu = false.obs; + RxBool enableShowDanmaku = Pref.enableShowDanmaku.obs; - bool autoPiP = - GStorage.setting.get(SettingBoxKey.autoPiP, defaultValue: false); + late final bool autoPiP = Pref.autoPiP; void enterPip() { if (Get.currentRoute.startsWith('/video')) { @@ -258,82 +260,97 @@ class PlPlayerController { } /// 弹幕权重 - int danmakuWeight = 0; - late RuleFilter filters; + late int danmakuWeight = Pref.danmakuWeight; + late RuleFilter filters = Pref.danmakuFilterRule; // 关联弹幕控制器 DanmakuController? danmakuController; bool showDanmaku = true; Set dmState = {}; - late final mergeDanmaku = GStorage.mergeDanmaku; + late final mergeDanmaku = Pref.mergeDanmaku; late final String midHash = Crc32Xz() .convert(utf8.encode(Accounts.main.mid.toString())) .toRadixString(16); // 弹幕相关配置 - late Set blockTypes; - late double showArea; - late double opacity; - late double fontSize; - late double fontSizeFS; - late double strokeWidth; - late int fontWeight; - late bool massiveMode; - late double danmakuDuration; - late double danmakuStaticDuration; - late List speedList; - late bool enableAutoLongPressSpeed = false; - late bool enableLongShowControl; - double subtitleFontScale = 1.0; - double subtitleFontScaleFS = 1.5; - late double danmakuLineHeight = GStorage.danmakuLineHeight; - late int subtitlePaddingH = GStorage.subtitlePaddingH; - late int subtitlePaddingB = GStorage.subtitlePaddingB; - late double subtitleBgOpaticy = GStorage.subtitleBgOpaticy; - late bool showVipDanmaku = GStorage.showVipDanmaku; - late bool showSpecialDanmaku = GStorage.showSpecialDanmaku; - late double subtitleStrokeWidth = GStorage.subtitleStrokeWidth; - late int subtitleFontWeight = GStorage.subtitleFontWeight; + late Set blockTypes = Pref.danmakuBlockType; + late double showArea = Pref.danmakuShowArea; + late double danmakuOpacity = Pref.danmakuOpacity; + late double danmakuFontScale = Pref.danmakuFontScale; + late double danmakuFontScaleFS = Pref.danmakuFontScaleFS; + late double strokeWidth = Pref.strokeWidth; + late int fontWeight = Pref.fontWeight; + late bool massiveMode = Pref.danmakuMassiveMode; + late double danmakuDuration = Pref.danmakuDuration; + late double danmakuStaticDuration = Pref.danmakuStaticDuration; + late List speedList = Pref.speedList; + late bool enableAutoLongPressSpeed = Pref.enableAutoLongPressSpeed; + late bool enableLongShowControl = Pref.enableLongShowControl; + late double subtitleFontScale = Pref.subtitleFontScale; + late double subtitleFontScaleFS = Pref.subtitleFontScaleFS; + late double danmakuLineHeight = Pref.danmakuLineHeight; + late int subtitlePaddingH = Pref.subtitlePaddingH; + late int subtitlePaddingB = Pref.subtitlePaddingB; + late double subtitleBgOpaticy = Pref.subtitleBgOpaticy; + late bool showVipDanmaku = Pref.showVipDanmaku; + late bool showSpecialDanmaku = Pref.showSpecialDanmaku; + late double subtitleStrokeWidth = Pref.subtitleStrokeWidth; + late int subtitleFontWeight = Pref.subtitleFontWeight; // sponsor block - late final bool enableSponsorBlock = - setting.get(SettingBoxKey.enableSponsorBlock, defaultValue: false); - late final double blockLimit = GStorage.blockLimit; - late final List> blockSettings = - GStorage.blockSettings; - late final List blockColor = GStorage.blockColor; + late final bool enableSponsorBlock = Pref.enableSponsorBlock; + late final double blockLimit = Pref.blockLimit; + late final blockSettings = Pref.blockSettings; + late final List blockColor = Pref.blockColor; late final Set enableList = blockSettings .where((item) => item.second != SkipType.disable) .map((item) => item.first.name) .toSet(); - late final blockServer = GStorage.blockServer; + late final blockServer = Pref.blockServer; // settings - late final showFSActionItem = GStorage.showFSActionItem; - late final enableShrinkVideoSize = GStorage.enableShrinkVideoSize; - late final darkVideoPage = GStorage.darkVideoPage; - late final enableSlideVolumeBrightness = GStorage.enableSlideVolumeBrightness; - late final enableSlideFS = GStorage.enableSlideFS; - late final enableDragSubtitle = GStorage.enableDragSubtitle; - late final fastForBackwardDuration = GStorage.fastForBackwardDuration; + late final showFSActionItem = Pref.showFSActionItem; + late final enableShrinkVideoSize = Pref.enableShrinkVideoSize; + late final darkVideoPage = Pref.darkVideoPage; + late final enableSlideVolumeBrightness = Pref.enableSlideVolumeBrightness; + late final enableSlideFS = Pref.enableSlideFS; + late final enableDragSubtitle = Pref.enableDragSubtitle; + late final fastForBackwardDuration = Pref.fastForBackwardDuration; - late final horizontalSeasonPanel = GStorage.horizontalSeasonPanel; - late final preInitPlayer = GStorage.preInitPlayer; - late final showRelatedVideo = GStorage.showRelatedVideo; - late final showVideoReply = GStorage.showVideoReply; - late final showBangumiReply = GStorage.showBangumiReply; - late final reverseFromFirst = GStorage.reverseFromFirst; - late final horizontalPreview = GStorage.horizontalPreview; - late final showDmChart = GStorage.showDmChart; + late final horizontalSeasonPanel = Pref.horizontalSeasonPanel; + late final preInitPlayer = Pref.preInitPlayer; + late final showRelatedVideo = Pref.showRelatedVideo; + late final showVideoReply = Pref.showVideoReply; + late final showBangumiReply = Pref.showBangumiReply; + late final reverseFromFirst = Pref.reverseFromFirst; + late final horizontalPreview = Pref.horizontalPreview; + late final showDmChart = Pref.showDmChart; + + late final bool autoExitFullscreen = Pref.autoExitFullscreen; + late final bool autoPlayEnable = Pref.autoPlayEnable; + late final bool enableVerticalExpand = Pref.enableVerticalExpand; + late final bool pipNoDanmaku = Pref.pipNoDanmaku; + late final bool removeSafeArea = Pref.removeSafeArea; int? cacheVideoQa; late int cacheAudioQa; bool enableHeart = true; - bool enableHA = - GStorage.setting.get(SettingBoxKey.enableHA, defaultValue: true); - String hwdec = GStorage.hardwareDecoding; + late final bool enableHA = Pref.enableHA; + late final String hwdec = Pref.hardwareDecoding; + + late final defaultBtmProgressBehavior = + BtmProgressBehavior.values[Pref.btmProgressBehavior]; + late final enableQuickDouble = Pref.enableQuickDouble; + late final fullScreenGestureReverse = Pref.fullScreenGestureReverse; + + late final isRelative = Pref.useRelativeSlide; + late final offset = + isRelative ? Pref.sliderDuration / 100 : Pref.sliderDuration * 1000; + + num get sliderScale => + isRelative ? duration.value.inMilliseconds * offset : offset; // 播放顺序相关 - PlayRepeat playRepeat = PlayRepeat.pause; + late PlayRepeat playRepeat = PlayRepeat.values[Pref.playRepeat]; TextStyle get subTitleStyle => TextStyle( height: 1.5, @@ -442,60 +459,10 @@ class PlPlayerController { await _instance?.setVolume(volumeNew, videoPlayerVolume: videoPlayerVolume); } - Box get video => GStorage.video; + Box video = GStorage.video; // 添加一个私有构造函数 PlPlayerController._() { - _videoType = videoType; - isOpenDanmu.value = - setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: true); - danmakuWeight = setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0); - filters = GStorage.danmakuFilterRule; - blockTypes = - (setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []) - as Iterable) - .cast() - .toSet(); - showArea = setting.get(SettingBoxKey.danmakuShowArea, defaultValue: 0.5); - // 不透明度 - opacity = setting.get(SettingBoxKey.danmakuOpacity, defaultValue: 1.0); - // 字体大小 - fontSize = setting.get(SettingBoxKey.danmakuFontScale, defaultValue: 1.0); - // 全屏字体大小 - fontSizeFS = GStorage.danmakuFontScaleFS; - subtitleFontScale = GStorage.subtitleFontScale; - subtitleFontScaleFS = GStorage.subtitleFontScaleFS; - massiveMode = GStorage.danmakuMassiveMode; - // 弹幕时间 - danmakuDuration = - setting.get(SettingBoxKey.danmakuDuration, defaultValue: 7.0); - danmakuStaticDuration = - setting.get(SettingBoxKey.danmakuStaticDuration, defaultValue: 4.0); - // 描边粗细 - strokeWidth = setting.get(SettingBoxKey.strokeWidth, defaultValue: 1.5); - // 弹幕字体粗细 - fontWeight = setting.get(SettingBoxKey.fontWeight, defaultValue: 5); - playRepeat = PlayRepeat.values.toList().firstWhere( - (e) => - e.value == - video.get(VideoBoxKey.playRepeat, - defaultValue: PlayRepeat.pause.value), - ); - _playbackSpeed.value = - video.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); - enableAutoLongPressSpeed = setting - .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); - // 后台播放 - _continuePlayInBackground.value = setting - .get(SettingBoxKey.continuePlayInBackground, defaultValue: false); - if (!enableAutoLongPressSpeed) { - _longPressSpeed.value = - video.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 3.0); - } - enableLongShowControl = - setting.get(SettingBoxKey.enableLongShowControl, defaultValue: false); - speedList = GStorage.speedList; - if (!Accounts.get(AccountType.heartbeat).isLogin || GStorage.localCache.get(LocalCacheKey.historyPause) == true) { enableHeart = false; @@ -663,7 +630,7 @@ class PlPlayerController { bool get _isPgc => Get.parameters['type'] == '1' || Get.parameters['type'] == '4'; - late int superResolutionType = _isPgc ? GStorage.superResolutionType : 0; + late int superResolutionType = _isPgc ? Pref.superResolutionType : 0; Future setShader([int? type, NativePlayer? pp]) async { if (type == null) { type ??= superResolutionType; @@ -714,27 +681,26 @@ class PlPlayerController { _heartDuration = 0; _position.value = Duration.zero; // 初始化时清空弹幕,防止上次重叠 - if (danmakuController != null) { - danmakuController!.clear(); - } - int bufferSize = - setting.get(SettingBoxKey.expandBuffer, defaultValue: false) - ? (videoType.value == 'live' ? 64 * 1024 * 1024 : 32 * 1024 * 1024) - : (videoType.value == 'live' ? 16 * 1024 * 1024 : 4 * 1024 * 1024); + danmakuController?.clear(); Player player = _videoPlayerController ?? Player( configuration: PlayerConfiguration( // 默认缓冲 4M 大小 - bufferSize: bufferSize, + bufferSize: Pref.expandBuffer + ? (videoType.value == 'live' + ? 64 * 1024 * 1024 + : 32 * 1024 * 1024) + : (videoType.value == 'live' + ? 16 * 1024 * 1024 + : 4 * 1024 * 1024), ), ); var pp = player.platform as NativePlayer; - // 解除倍速限制 - if (_isPgc) { - setShader(superResolutionType, pp); - } if (_videoPlayerController == null) { - String audioNormalization = GStorage.audioNormalization; + if (_isPgc) { + setShader(superResolutionType, pp); + } + String audioNormalization = Pref.audioNormalization; audioNormalization = switch (audioNormalization) { '0' => '', '1' => ',${AudioNormalization.dynaudnorm.param}', @@ -745,25 +711,21 @@ class PlPlayerController { "af", "scaletempo2=max-speed=8$audioNormalization", ); + if (Platform.isAndroid) { + await pp.setProperty("volume-max", "100"); + String ao = + Pref.useOpenSLES ? "opensles,audiotrack" : "audiotrack,opensles"; + await pp.setProperty("ao", ao); + } + // video-sync=display-resample + await pp.setProperty("video-sync", Pref.videoSync); + // vo=gpu-next & gpu-context=android & gpu-api=opengl + // await pp.setProperty("vo", "gpu-next"); + // await pp.setProperty("gpu-context", "android"); + // await pp.setProperty("gpu-api", "opengl"); + await player.setAudioTrack(AudioTrack.auto()); } - // 音量不一致 - if (Platform.isAndroid) { - await pp.setProperty("volume-max", "100"); - String ao = setting.get(SettingBoxKey.useOpenSLES, defaultValue: true) - ? "opensles,audiotrack" - : "audiotrack,opensles"; - await pp.setProperty("ao", ao); - } - // video-sync=display-resample - await pp.setProperty("video-sync", - setting.get(SettingBoxKey.videoSync, defaultValue: 'display-resample')); - // // vo=gpu-next & gpu-context=android & gpu-api=opengl - // await pp.setProperty("vo", "gpu-next"); - // await pp.setProperty("gpu-context", "android"); - // await pp.setProperty("gpu-api", "opengl"); - await player.setAudioTrack( - AudioTrack.auto(), - ); + // 音轨 if (dataSource.audioSource?.isNotEmpty == true) { await pp.setProperty( @@ -777,7 +739,7 @@ class PlPlayerController { } // 字幕 - if (dataSource.subFiles != '' && dataSource.subFiles != null) { + if (dataSource.subFiles?.isNotEmpty == true) { await pp.setProperty( 'sub-files', UniversalPlatform.isWindows @@ -881,10 +843,9 @@ class PlPlayerController { } } + late final bool enableAutoEnter = Pref.enableAutoEnter; Future autoEnterFullscreen() async { - bool autoEnterFullscreen = GStorage.setting - .get(SettingBoxKey.enableAutoEnter, defaultValue: false); - if (autoEnterFullscreen) { + if (enableAutoEnter) { Future.delayed(const Duration(milliseconds: 500), () { if (dataStatus.status.value != DataStatus.loaded) { _dataListenerForEnterFullscreen = dataStatus.status.listen((status) { @@ -1115,10 +1076,10 @@ class PlPlayerController { } // 还原默认速度 + double playSpeedDefault = Pref.playSpeedDefault; Future setDefaultSpeed() async { - double speed = video.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); - await _videoPlayerController?.setRate(speed); - _playbackSpeed.value = speed; + await _videoPlayerController?.setRate(playSpeedDefault); + _playbackSpeed.value = playSpeedDefault; } /// 播放视频 @@ -1281,8 +1242,8 @@ class PlPlayerController { } /// 读取fit + int fitValue = Pref.cacheVideoFit; Future getVideoFit() async { - int fitValue = video.get(VideoBoxKey.cacheVideoFit, defaultValue: 1); var attr = BoxFit.values[fitValue]; // 由于none与scaleDown涉及视频原始尺寸,需要等待视频加载后再设置,否则尺寸会变为0,出现错误; if (attr == BoxFit.none || attr == BoxFit.scaleDown) { @@ -1291,8 +1252,6 @@ class PlPlayerController { _dataListenerForVideoFit = dataStatus.status.listen((status) { if (status == DataStatus.loaded) { _dataListenerForVideoFit.cancel(); - int fitValue = - video.get(VideoBoxKey.cacheVideoFit, defaultValue: 1); var attr = BoxFit.values[fitValue]; if (attr == BoxFit.none || attr == BoxFit.scaleDown) { _videoFit.value = attr; @@ -1309,17 +1268,10 @@ class PlPlayerController { /// 设置后台播放 Future setBackgroundPlay(bool val) async { + videoPlayerServiceHandler.enableBackgroundPlay = val; setting.put(SettingBoxKey.enableBackgroundPlay, val); - videoPlayerServiceHandler.revalidateSetting(); } - /// 读取亮度 - // Future getVideoBrightness() async { - // double brightnessValue = - // video.get(VideoBoxKey.videoBrightness, defaultValue: 0.5); - // setBrightness(brightnessValue); - // } - set controls(bool visible) { _showControls.value = visible; _timer?.cancel(); @@ -1369,9 +1321,8 @@ class PlPlayerController { updateSubtitleStyle(); } - late final FullScreenMode mode = FullScreenModeCode.fromCode( - setting.get(SettingBoxKey.fullScreenMode, defaultValue: 0)); - late final horizontalScreen = GStorage.horizontalScreen; + late final FullScreenMode mode = FullScreenMode.values[Pref.fullScreenMode]; + late final horizontalScreen = Pref.horizontalScreen; // 全屏 void triggerFullScreen({bool status = true, int duration = 500}) { @@ -1403,8 +1354,6 @@ class PlPlayerController { await landScape(); } } else if (isFullScreen.value && !status) { - late bool removeSafeArea = setting - .get(SettingBoxKey.videoPlayerRemoveSafeArea, defaultValue: false); if (Get.currentRoute.startsWith('/liveRoom') || !removeSafeArea) { showStatusBar(); } @@ -1500,7 +1449,7 @@ class PlPlayerController { void setPlayRepeat(PlayRepeat type) { playRepeat = type; - video.put(VideoBoxKey.playRepeat, type.value); + video.put(VideoBoxKey.playRepeat, type.index); } void putDanmakuSettings() { @@ -1508,9 +1457,9 @@ class PlPlayerController { ..put(SettingBoxKey.danmakuWeight, danmakuWeight) ..put(SettingBoxKey.danmakuBlockType, blockTypes.toList()) ..put(SettingBoxKey.danmakuShowArea, showArea) - ..put(SettingBoxKey.danmakuOpacity, opacity) - ..put(SettingBoxKey.danmakuFontScale, fontSize) - ..put(SettingBoxKey.danmakuFontScaleFS, fontSizeFS) + ..put(SettingBoxKey.danmakuOpacity, danmakuOpacity) + ..put(SettingBoxKey.danmakuFontScale, danmakuFontScale) + ..put(SettingBoxKey.danmakuFontScaleFS, danmakuFontScaleFS) ..put(SettingBoxKey.danmakuDuration, danmakuDuration) ..put(SettingBoxKey.danmakuStaticDuration, danmakuStaticDuration) ..put(SettingBoxKey.strokeWidth, strokeWidth) @@ -1596,7 +1545,7 @@ class PlPlayerController { onlyPlayAudio.value ? VideoTrack.no() : VideoTrack.auto()); } - late final showSeekPreview = GStorage.showSeekPreview; + late final showSeekPreview = Pref.showSeekPreview; late bool _isQueryingVideoShot = false; Map? videoShot; late final RxBool showPreview = false.obs; diff --git a/lib/plugin/pl_player/models/bottom_progress_behavior.dart b/lib/plugin/pl_player/models/bottom_progress_behavior.dart index 95e4de444..8db45c63a 100644 --- a/lib/plugin/pl_player/models/bottom_progress_behavior.dart +++ b/lib/plugin/pl_player/models/bottom_progress_behavior.dart @@ -1,18 +1,10 @@ enum BtmProgressBehavior { - alwaysShow, - alwaysHide, - onlyShowFullScreen, - onlyHideFullScreen, -} + alwaysShow('始终展示'), + alwaysHide('始终隐藏'), + onlyShowFullScreen('仅全屏时展示'), + onlyHideFullScreen('仅全屏时隐藏'), + ; -extension BtmProgresBehaviorDesc on BtmProgressBehavior { - String get description => const ['始终展示', '始终隐藏', '仅全屏时展示', '仅全屏时隐藏'][index]; -} - -extension BtmProgresBehaviorCode on BtmProgressBehavior { - int get code => index; - - static BtmProgressBehavior fromCode(int code) { - return BtmProgressBehavior.values[code]; - } + final String desc; + const BtmProgressBehavior(this.desc); } diff --git a/lib/plugin/pl_player/models/fullscreen_mode.dart b/lib/plugin/pl_player/models/fullscreen_mode.dart index 28312addf..7eced72f4 100644 --- a/lib/plugin/pl_player/models/fullscreen_mode.dart +++ b/lib/plugin/pl_player/models/fullscreen_mode.dart @@ -1,34 +1,19 @@ // 全屏模式 enum FullScreenMode { // 根据内容自适应 - auto, + auto('按视频方向(默认)'), // 不改变当前方向 - none, + none('不改变当前方向'), // 始终竖屏 - vertical, + vertical('强制竖屏'), // 始终横屏 - horizontal, + horizontal('强制竖屏'), // 屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏 - ratio, + ratio('屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏'), // 强制重力转屏(仅安卓) - gravity, -} + gravity('忽略系统方向锁定,强制按重力转屏(仅安卓)'), + ; -extension FullScreenModeDesc on FullScreenMode { - String get description => const [ - '按视频方向(默认)', - '不改变当前方向', - '强制竖屏', - '强制横屏', - '屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏', - '忽略系统方向锁定,强制按重力转屏(仅安卓)' - ][index]; -} - -extension FullScreenModeCode on FullScreenMode { - int get code => index; - - static FullScreenMode fromCode(int code) { - return FullScreenMode.values[code]; - } + final String desc; + const FullScreenMode(this.desc); } diff --git a/lib/plugin/pl_player/models/play_repeat.dart b/lib/plugin/pl_player/models/play_repeat.dart index 8e3272340..e01aa36c8 100644 --- a/lib/plugin/pl_player/models/play_repeat.dart +++ b/lib/plugin/pl_player/models/play_repeat.dart @@ -1,28 +1,11 @@ enum PlayRepeat { - pause, - listOrder, - singleCycle, - listCycle, - autoPlayRelated, -} + pause('播完暂停'), + listOrder('顺序播放'), + singleCycle('单个循环'), + listCycle('列表循环'), + autoPlayRelated('自动连播'), + ; -extension PlayRepeatExtension on PlayRepeat { - static const List _descList = [ - '播完暂停', - '顺序播放', - '单个循环', - '列表循环', - '自动连播', - ]; - String get description => _descList[index]; - - static const List _valueList = [ - 1, - 2, - 3, - 4, - 5, - ]; - double get value => _valueList[index]; - double get defaultValue => _valueList[1]; + final String desc; + const PlayRepeat(this.desc); } diff --git a/lib/plugin/pl_player/models/play_speed.dart b/lib/plugin/pl_player/models/play_speed.dart index cb18435b0..20cc2d2f3 100644 --- a/lib/plugin/pl_player/models/play_speed.dart +++ b/lib/plugin/pl_player/models/play_speed.dart @@ -1,39 +1,18 @@ enum PlaySpeed { - pointFive, - pointSevenFive, + pointFive(0.5), + pointSevenFive(0.75), - one, - onePointTwoFive, - onePointFive, - onePointSevenFive, + one(1.0), + onePointTwoFive(1.25), + onePointFive(1.5), + onePointSevenFive(1.75), - two, - three, -} - -extension PlaySpeedExtension on PlaySpeed { - static const List _descList = [ - '0.5', - '0.75', - '正常', - '1.25', - '1.5', - '1.75', - '2.0', - '3.0' - ]; - String get description => _descList[index]; - - static const List _valueList = [ - 0.5, - 0.75, - 1.0, - 1.25, - 1.5, - 1.75, - 2.0, - 3.0, - ]; - double get value => _valueList[index]; - double get defaultValue => _valueList[3]; + two(2.0), + three(3.0), + ; + + final double value; + const PlaySpeed(this.value); + + String get desc => value == 1.0 ? '正常' : value.toString(); } diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart index 2db94fec0..b5e463243 100644 --- a/lib/plugin/pl_player/utils/fullscreen.dart +++ b/lib/plugin/pl_player/utils/fullscreen.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:auto_orientation/auto_orientation.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; @@ -55,8 +55,7 @@ Future verticalScreen() async { //全向 Future autoScreen() async { - if (!GStorage.setting - .get(SettingBoxKey.allowRotateScreen, defaultValue: true)) { + if (!Pref.allowRotateScreen) { return; } await SystemChrome.setPreferredOrientations([ diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index efdc53ba3..2ad0df72c 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -28,7 +28,6 @@ import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:fl_chart/fl_chart.dart'; @@ -107,21 +106,9 @@ class _PLVideoPlayerState extends State final RxBool _volumeInterceptEventStream = false.obs; late FullScreenMode mode; - late int defaultBtmProgressBehavior; - late bool enableQuickDouble; - late bool fullScreenGestureReverse; late final RxBool showRestoreScaleBtn = false.obs; - late final _isRelative = GStorage.useRelativeSlide; - late final _offset = _isRelative - ? GStorage.sliderDuration / 100 - : GStorage.sliderDuration * 1000; - - num get sliderScale => _isRelative - ? plPlayerController.duration.value.inMilliseconds * _offset - : _offset; - Offset _initialFocalPoint = Offset.zero; String? _gestureType; //播放器放缩 @@ -152,7 +139,7 @@ class _PLVideoPlayerState extends State } void doubleTapFuc(String type) { - if (!enableQuickDouble) { + if (!plPlayerController.enableQuickDouble) { onDoubleTapCenter(); return; } @@ -182,13 +169,6 @@ class _PLVideoPlayerState extends State videoController = plPlayerController.videoController!; videoIntroController = widget.videoIntroController; pgcIntroController = widget.pgcIntroController; - defaultBtmProgressBehavior = GStorage.setting.get( - SettingBoxKey.btmProgressBehavior, - defaultValue: BtmProgressBehavior.values.first.code); - enableQuickDouble = GStorage.setting - .get(SettingBoxKey.enableQuickDouble, defaultValue: true); - fullScreenGestureReverse = GStorage.setting - .get(SettingBoxKey.fullScreenGestureReverse, defaultValue: false); Future.microtask(() async { try { FlutterVolumeController.updateShowSystemUI(true); @@ -820,7 +800,8 @@ class _PLVideoPlayerState extends State final double width = renderBox.size.width; final Duration pos = Duration( milliseconds: curSliderPosition + - (sliderScale * delta.dx / width).round()); // TODO + (plPlayerController.sliderScale * delta.dx / width) + .round()); // TODO final Duration result = pos.clamp(Duration.zero, plPlayerController.duration.value); final height = renderBox.size.height * 0.125; @@ -897,14 +878,18 @@ class _PLVideoPlayerState extends State if (cumulativeDy > threshold) { _gestureType = 'center_down'; - if (isFullScreen ^ fullScreenGestureReverse) { - fullScreenTrigger(fullScreenGestureReverse); + if (isFullScreen ^ + plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + plPlayerController.fullScreenGestureReverse); } // if (kDebugMode) debugPrint('center_down:$cumulativeDy'); } else if (cumulativeDy < -threshold) { _gestureType = 'center_up'; - if (!isFullScreen ^ fullScreenGestureReverse) { - fullScreenTrigger(!fullScreenGestureReverse); + if (!isFullScreen ^ + plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + !plPlayerController.fullScreenGestureReverse); } // if (kDebugMode) debugPrint('center_up:$cumulativeDy'); } @@ -1007,14 +992,18 @@ class _PLVideoPlayerState extends State if (cumulativeDy > threshold) { _gestureType = 'center_down'; - if (isFullScreen ^ fullScreenGestureReverse) { - fullScreenTrigger(fullScreenGestureReverse); + if (isFullScreen ^ + plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + plPlayerController.fullScreenGestureReverse); } // if (kDebugMode) debugPrint('center_down:$cumulativeDy'); } else if (cumulativeDy < -threshold) { _gestureType = 'center_up'; - if (!isFullScreen ^ fullScreenGestureReverse) { - fullScreenTrigger(!fullScreenGestureReverse); + if (!isFullScreen ^ + plPlayerController.fullScreenGestureReverse) { + fullScreenTrigger( + !plPlayerController.fullScreenGestureReverse); } // if (kDebugMode) debugPrint('center_up:$cumulativeDy'); } @@ -1363,26 +1352,30 @@ class _PLVideoPlayerState extends State if (plPlayerController.showControls.value) { return const SizedBox.shrink(); } - if (defaultBtmProgressBehavior == - BtmProgressBehavior.alwaysHide.code) { - return const SizedBox.shrink(); - } - if (defaultBtmProgressBehavior == - BtmProgressBehavior.onlyShowFullScreen.code && - !isFullScreen) { - return const SizedBox.shrink(); - } else if (defaultBtmProgressBehavior == - BtmProgressBehavior.onlyHideFullScreen.code && - isFullScreen) { - return const SizedBox.shrink(); + + switch (plPlayerController.defaultBtmProgressBehavior) { + case BtmProgressBehavior.alwaysShow: + break; + case BtmProgressBehavior.alwaysHide: + return const SizedBox.shrink(); + case BtmProgressBehavior.onlyShowFullScreen: + if (!isFullScreen) { + return const SizedBox.shrink(); + } + case BtmProgressBehavior.onlyHideFullScreen: + if (isFullScreen) { + return const SizedBox.shrink(); + } } if (plPlayerController.videoType.value == 'live') { return const SizedBox.shrink(); } + if (value > max || max <= 0) { return const SizedBox.shrink(); } + return Positioned( bottom: -2.2, left: 0, diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index dbb59360d..13651f3a5 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -62,7 +62,7 @@ import 'package:PiliPlus/pages/webdav/view.dart'; import 'package:PiliPlus/pages/webview/view.dart'; import 'package:PiliPlus/pages/whisper/view.dart'; import 'package:PiliPlus/pages/whisper_detail/view.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -197,9 +197,10 @@ class CustomGetPage extends GetPage { super.transitionDuration, }) : super( curve: Curves.linear, - transition: GStorage.pageTransition, + transition: pageTransition, showCupertinoParallax: false, popGesture: false, fullscreenDialog: fullscreen, ); + static Transition pageTransition = Transition.values[Pref.pageTransition]; } diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index 4fae693e1..e3bae4be4 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -3,7 +3,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_info_model/result.dart'; import 'package:PiliPlus/models_new/video/video_detail/data.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:audio_service/audio_service.dart'; import 'package:get/get_utils/get_utils.dart'; @@ -25,17 +25,7 @@ Future initAudioService() async { class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; - bool enableBackgroundPlay = true; - // PlPlayerController player = PlPlayerController.getInstance(); - - VideoPlayerServiceHandler() { - revalidateSetting(); - } - - void revalidateSetting() { - enableBackgroundPlay = GStorage.setting - .get(SettingBoxKey.enableBackgroundPlay, defaultValue: true); - } + bool enableBackgroundPlay = Pref.enableBackgroundPlay; @override Future play() async { diff --git a/lib/utils/accounts.dart b/lib/utils/accounts.dart new file mode 100644 index 000000000..a42d2e54e --- /dev/null +++ b/lib/utils/accounts.dart @@ -0,0 +1,119 @@ +import 'dart:io'; + +import 'package:PiliPlus/models/common/account_type.dart'; +import 'package:PiliPlus/pages/mine/controller.dart'; +import 'package:PiliPlus/utils/accounts/account.dart'; +import 'package:PiliPlus/utils/login_utils.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:cookie_jar/cookie_jar.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart'; + +class Accounts { + static late final Box account; + static final Map accountMode = {}; + static Account get main => accountMode[AccountType.main]!; + // static set main(Account account) => set(AccountType.main, account); + + static Future init() async { + account = await Hive.openBox('account', + compactionStrategy: (int entries, int deletedEntries) { + return deletedEntries > 2; + }); + await _migrate(); + } + + static Future _migrate() async { + final Directory tempDir = await getApplicationSupportDirectory(); + final String tempPath = "${tempDir.path}/.plpl/"; + final Directory dir = Directory(tempPath); + if (dir.existsSync()) { + if (kDebugMode) debugPrint('migrating...'); + final cookieJar = + PersistCookieJar(ignoreExpires: true, storage: FileStorage(tempPath)); + await cookieJar.forceInit(); + final cookies = DefaultCookieJar(ignoreExpires: true) + ..domainCookies.addAll(cookieJar.domainCookies); + final localAccessKey = + GStorage.localCache.get('accessKey', defaultValue: {}); + + final isLogin = + cookies.domainCookies['bilibili.com']?['/']?['SESSDATA'] != null; + + await Future.wait([ + GStorage.localCache.delete('accessKey'), + GStorage.localCache.delete('danmakuFilterRule'), + GStorage.localCache.delete('blackMidsList'), + dir.delete(recursive: true), + if (isLogin) + LoginAccount(cookies, localAccessKey['value'], + localAccessKey['refresh'], AccountType.values.toSet()) + .onChange() + ]); + if (kDebugMode) debugPrint('migrated successfully'); + } + } + + static void refresh() { + for (var a in account.values) { + for (var t in a.type) { + accountMode[t] = a; + } + } + for (var type in AccountType.values) { + accountMode[type] ??= AnonymousAccount(); + } + // await Future.wait((accountMode.values.toSet() + // ..retainWhere((i) => !i.activited)) + // .map((i) => Request.buvidActive(i))); + } + + static Future clear() async { + await account.clear(); + for (var i in AccountType.values) { + accountMode[i] = AnonymousAccount(); + } + await AnonymousAccount().delete(); + // Request.buvidActive(AnonymousAccount()); + } + + static void close() { + account + ..compact() + ..close(); + } + + static Future deleteAll(Set accounts) async { + var isloginMain = Accounts.main.isLogin; + Accounts.accountMode + .updateAll((_, a) => accounts.contains(a) ? AnonymousAccount() : a); + await Future.wait(accounts.map((i) => i.delete())); + if (isloginMain && !Accounts.main.isLogin) { + await LoginUtils.onLogoutMain(); + } + } + + static Future set(AccountType key, Account account) async { + await (accountMode[key]?..type.remove(key))?.onChange(); + accountMode[key] = account..type.add(key); + await account.onChange(); + // if (!account.activited) await Request.buvidActive(account); + switch (key) { + case AccountType.main: + await (account.isLogin + ? LoginUtils.onLoginMain() + : LoginUtils.onLogoutMain()); + break; + case AccountType.heartbeat: + MineController.anonymity.value = !account.isLogin; + break; + default: + break; + } + } + + static Account get(AccountType key) { + return accountMode[key]!; + } +} diff --git a/lib/utils/accounts/account.dart b/lib/utils/accounts/account.dart index 04e01ccb0..9fd4801d4 100644 --- a/lib/utils/accounts/account.dart +++ b/lib/utils/accounts/account.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/models/common/account_type.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:hive/hive.dart'; diff --git a/lib/utils/accounts/account_manager/account_mgr.dart b/lib/utils/accounts/account_manager/account_mgr.dart index 8e1f6a500..60c664682 100644 --- a/lib/utils/accounts/account_manager/account_mgr.dart +++ b/lib/utils/accounts/account_manager/account_mgr.dart @@ -5,10 +5,11 @@ import 'dart:io'; import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/models/common/account_type.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -72,7 +73,7 @@ class AccountManager extends Interceptor { AccountManager(); - String blockServer = GStorage.blockServer; + String blockServer = Pref.blockServer; static String getCookies(List cookies) { // Sort cookies by path (longer path first). diff --git a/lib/utils/data.dart b/lib/utils/data.dart index 6fc1fd040..5084dc8dd 100644 --- a/lib/utils/data.dart +++ b/lib/utils/data.dart @@ -1,5 +1,7 @@ import 'package:PiliPlus/http/user.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; class Data { static Future init() async { diff --git a/lib/utils/feed_back.dart b/lib/utils/feed_back.dart index d726633ac..d43a9ca6d 100644 --- a/lib/utils/feed_back.dart +++ b/lib/utils/feed_back.dart @@ -1,7 +1,7 @@ -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/services.dart' show HapticFeedback; -bool enableFeedback = GStorage.feedBackEnable; +bool enableFeedback = Pref.feedBackEnable; void feedBack() { if (enableFeedback) { HapticFeedback.lightImpact(); diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index 6174754b9..ea544f5c4 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -1,7 +1,7 @@ -class GlobalData { - int imgQuality = 10; +import 'package:PiliPlus/utils/storage_pref.dart'; - int replyLengthLimit = 6; +class GlobalData { + int imgQuality = Pref.picQuality; num? coins; @@ -11,6 +11,10 @@ class GlobalData { } } + Set blackMids = Pref.blackMids; + + bool dynamicsWaterfallFlow = Pref.dynamicsWaterfallFlow; + // 私有构造函数 GlobalData._(); diff --git a/lib/utils/grid.dart b/lib/utils/grid.dart index d0b392406..1cbe7f995 100644 --- a/lib/utils/grid.dart +++ b/lib/utils/grid.dart @@ -1,12 +1,12 @@ import 'dart:math'; import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; class Grid { - static double smallCardWidth = GStorage.smallCardWidth; + static final double smallCardWidth = Pref.smallCardWidth; static SliverGridDelegateWithExtentAndRatio videoCardHDelegate( BuildContext context, diff --git a/lib/utils/login_utils.dart b/lib/utils/login_utils.dart index 437052297..2ebec2164 100644 --- a/lib/utils/login_utils.dart +++ b/lib/utils/login_utils.dart @@ -14,6 +14,7 @@ import 'package:PiliPlus/pages/media/controller.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/pages/pgc/controller.dart'; import 'package:PiliPlus/services/account_service.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web; diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index c680b4edf..9d5b9e7df 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -21,7 +21,7 @@ import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/url_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:floating/floating.dart'; @@ -540,7 +540,7 @@ class PageUtils { String url, { bool off = false, }) { - if (GStorage.openInBrowser) { + if (Pref.openInBrowser) { launchURL(url); } else { if (off) { @@ -579,7 +579,7 @@ class PageUtils { bool inApp = false, Map? parameters, }) async { - if (!inApp && GStorage.openInBrowser) { + if (!inApp && Pref.openInBrowser) { if (!await PiliScheme.routePushFromUrl(url, selfHandle: true)) { launchURL(url); } diff --git a/lib/utils/recommend_filter.dart b/lib/utils/recommend_filter.dart index 7c7d2902a..8a7fee903 100644 --- a/lib/utils/recommend_filter.dart +++ b/lib/utils/recommend_filter.dart @@ -1,33 +1,15 @@ import 'package:PiliPlus/models/model_video.dart'; -import 'package:PiliPlus/utils/storage.dart'; -import 'package:hive/hive.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; class RecommendFilter { - static late int minDurationForRcmd; - static late int minPlayForRcmd; - static late int minLikeRatioForRecommend; - static late bool exemptFilterForFollowed; - static late bool applyFilterToRelatedVideos; + static int minDurationForRcmd = Pref.minDurationForRcmd; + static int minPlayForRcmd = Pref.minPlayForRcmd; + static int minLikeRatioForRecommend = Pref.minLikeRatioForRecommend; + static bool exemptFilterForFollowed = Pref.exemptFilterForFollowed; + static bool applyFilterToRelatedVideos = Pref.applyFilterToRelatedVideos; static RegExp rcmdRegExp = - RegExp(GStorage.banWordForRecommend, caseSensitive: false); - - RecommendFilter() { - update(); - } - - static Box get setting => GStorage.setting; - - static void update() { - minDurationForRcmd = - setting.get(SettingBoxKey.minDurationForRcmd, defaultValue: 0); - minPlayForRcmd = setting.get(SettingBoxKey.minPlayForRcmd, defaultValue: 0); - minLikeRatioForRecommend = - setting.get(SettingBoxKey.minLikeRatioForRecommend, defaultValue: 0); - exemptFilterForFollowed = - setting.get(SettingBoxKey.exemptFilterForFollowed, defaultValue: true); - applyFilterToRelatedVideos = setting - .get(SettingBoxKey.applyFilterToRelatedVideos, defaultValue: true); - } + RegExp(Pref.banWordForRecommend, caseSensitive: false); + static bool enableFilter = rcmdRegExp.pattern.isNotEmpty; static bool filter(BaseVideoItemModel videoItem) { //由于相关视频中没有已关注标签,只能视为非关注视频 @@ -48,7 +30,7 @@ class RecommendFilter { } static bool filterTitle(String title) { - return (rcmdRegExp.pattern.isNotEmpty && rcmdRegExp.hasMatch(title)); + return (enableFilter && rcmdRegExp.hasMatch(title)); } static bool filterAll(BaseVideoItemModel videoItem) { diff --git a/lib/utils/reply_utils.dart b/lib/utils/reply_utils.dart index 3e2abaa57..6dab6a29b 100644 --- a/lib/utils/reply_utils.dart +++ b/lib/utils/reply_utils.dart @@ -7,10 +7,10 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/reply.dart'; import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; import 'package:PiliPlus/models_new/reply/data.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/storage.dart' show Accounts; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; diff --git a/lib/utils/request_utils.dart b/lib/utils/request_utils.dart index 4a250bc4f..70d24505f 100644 --- a/lib/utils/request_utils.dart +++ b/lib/utils/request_utils.dart @@ -23,8 +23,9 @@ import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/pages/dynamics_tab/controller.dart'; import 'package:PiliPlus/pages/group_panel/view.dart'; import 'package:PiliPlus/pages/later/controller.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/feed_back.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -289,7 +290,7 @@ class RequestUtils { static Future checkCreatedDyn( {dynamic id, String? dynText, bool isManual = false}) async { - if (isManual || GStorage.enableCreateDynAntifraud) { + if (isManual || Pref.enableCreateDynAntifraud) { try { if (id != null) { if (!isManual) { diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index ee373bd60..a5a8ae4eb 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -1,47 +1,16 @@ import 'dart:convert'; import 'dart:io'; -import 'dart:math'; -import 'package:PiliPlus/common/widgets/pair.dart'; -import 'package:PiliPlus/common/widgets/refresh_indicator.dart' - show kDragContainerExtentPercentage, displacement; -import 'package:PiliPlus/http/constants.dart'; -import 'package:PiliPlus/models/common/account_type.dart'; -import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart'; -import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; -import 'package:PiliPlus/models/common/home_tab_type.dart'; -import 'package:PiliPlus/models/common/member/tab_type.dart'; -import 'package:PiliPlus/models/common/msg/msg_unread_type.dart'; -import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart'; -import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart'; -import 'package:PiliPlus/models/common/theme/theme_type.dart'; -import 'package:PiliPlus/models/common/video/audio_quality.dart'; -import 'package:PiliPlus/models/common/video/cdn_type.dart'; -import 'package:PiliPlus/models/common/video/live_quality.dart'; -import 'package:PiliPlus/models/common/video/subtitle_pref_type.dart'; -import 'package:PiliPlus/models/common/video/video_decode_type.dart'; -import 'package:PiliPlus/models/common/video/video_quality.dart'; import 'package:PiliPlus/models/model_owner.dart'; -import 'package:PiliPlus/models/user/danmaku_rule.dart'; import 'package:PiliPlus/models/user/danmaku_rule_adapter.dart'; import 'package:PiliPlus/models/user/info.dart'; -import 'package:PiliPlus/pages/mine/controller.dart'; -import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart'; -import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; -import 'package:PiliPlus/utils/accounts/account.dart'; +import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account_adapter.dart'; import 'package:PiliPlus/utils/accounts/account_type_adapter.dart'; import 'package:PiliPlus/utils/accounts/cookie_jar_adapter.dart'; -import 'package:PiliPlus/utils/global_data.dart'; -import 'package:PiliPlus/utils/login_utils.dart'; import 'package:PiliPlus/utils/set_int_adapter.dart'; -import 'package:cookie_jar/cookie_jar.dart'; -import 'package:flutter/foundation.dart' show kDebugMode; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:uuid/uuid.dart'; class GStorage { static late final Box userInfo; @@ -50,510 +19,6 @@ class GStorage { static late final Box setting; static late final Box video; - static List get speedList => List.from( - video.get( - VideoBoxKey.speedsList, - defaultValue: const [0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 3.0], - ), - ); - - static List get tabbarSort => - List.from(setting.get(SettingBoxKey.tabBarSort) ?? - HomeTabType.values.map((item) => item.index).toList()); - - static List> get blockSettings { - List list = List.from(setting.get(SettingBoxKey.blockSettings) ?? - List.generate(SegmentType.values.length, (_) => 1)); - return SegmentType.values - .map((item) => Pair( - first: item, - second: SkipType.values[list[item.index]], - )) - .toList(); - } - - static List get blockColor { - List list = List.from( - setting.get(SettingBoxKey.blockColor) ?? - List.generate(SegmentType.values.length, (_) => '')); - return SegmentType.values - .map((item) => list[item.index].isNotEmpty - ? Color( - int.tryParse('FF${list[item.index]}', radix: 16) ?? 0xFF000000) - : item.color) - .toList(); - } - - static bool get hiddenSettingUnlocked => - setting.get(SettingBoxKey.hiddenSettingUnlocked, defaultValue: false); - - static bool get feedBackEnable => - setting.get(SettingBoxKey.feedBackEnable, defaultValue: false); - - static double get toastOpacity => - setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0); - - static int get picQuality => - setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); - - static ThemeType get themeType => ThemeType.values[themeTypeInt]; - - static DynamicBadgeMode get dynamicBadgeType => - DynamicBadgeMode.values[setting.get( - SettingBoxKey.dynamicBadgeMode, - defaultValue: DynamicBadgeMode.number.index, - )]; - - static DynamicBadgeMode get msgBadgeMode => - DynamicBadgeMode.values[setting.get( - SettingBoxKey.msgBadgeMode, - defaultValue: DynamicBadgeMode.number.index, - )]; - - static Set get msgUnReadTypeV2 => - (setting.get(SettingBoxKey.msgUnReadTypeV2) as List?) - ?.map((index) => MsgUnReadType.values[index]) - .toSet() ?? - MsgUnReadType.values.toSet(); - - static int get defaultHomePage => - setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0); - - static int get previewQ => - setting.get(SettingBoxKey.previewQuality, defaultValue: 100); - - static double get smallCardWidth => - setting.get(SettingBoxKey.smallCardWidth, defaultValue: 240.0); - - static UpPanelPosition get upPanelPosition => - UpPanelPosition.values[setting.get(SettingBoxKey.upPanelPosition, - defaultValue: UpPanelPosition.leftFixed.index)]; - - static int get defaultFullScreenMode => - setting.get(SettingBoxKey.fullScreenMode, - defaultValue: FullScreenMode.values.first.code); - - static int get defaultBtmProgressBehavior => - setting.get(SettingBoxKey.btmProgressBehavior, - defaultValue: BtmProgressBehavior.values.first.code); - - static String get defaultSubtitlePreference => - setting.get(SettingBoxKey.subtitlePreference, - defaultValue: SubtitlePrefType.values.first.code); - - static bool get useRelativeSlide => - setting.get(SettingBoxKey.useRelativeSlide, defaultValue: false); - - static int get sliderDuration => - setting.get(SettingBoxKey.sliderDuration, defaultValue: 90); - - static int get defaultVideoQa => setting.get( - SettingBoxKey.defaultVideoQa, - defaultValue: VideoQuality.values.last.code, - ); - - static int get defaultVideoQaCellular => setting.get( - SettingBoxKey.defaultVideoQaCellular, - defaultValue: VideoQuality.high1080.code, - ); - - static int get defaultAudioQa => setting.get( - SettingBoxKey.defaultAudioQa, - defaultValue: AudioQuality.values.last.code, - ); - - static int get defaultAudioQaCellular => setting.get( - SettingBoxKey.defaultAudioQaCellular, - defaultValue: AudioQuality.k192.code, - ); - - static String get defaultDecode => setting.get( - SettingBoxKey.defaultDecode, - defaultValue: VideoDecodeFormatType.values.last.code, - ); - - static String get secondDecode => setting.get( - SettingBoxKey.secondDecode, - defaultValue: VideoDecodeFormatType.values[1].code, - ); - - static String get hardwareDecoding => setting.get( - SettingBoxKey.hardwareDecoding, - defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto', - ); - - static String get videoSync => setting.get( - SettingBoxKey.videoSync, - defaultValue: 'display-resample', - ); - - static String get defaultCDNService => setting.get( - SettingBoxKey.CDNService, - defaultValue: CDNService.backupUrl.code, - ); - - static String get banWordForRecommend => - setting.get(SettingBoxKey.banWordForRecommend, defaultValue: ''); - - static String get banWordForReply => - setting.get(SettingBoxKey.banWordForReply, defaultValue: ''); - - static String get banWordForZone => - setting.get(SettingBoxKey.banWordForZone, defaultValue: ''); - - static bool get appRcmd => - setting.get(SettingBoxKey.appRcmd, defaultValue: true); - - static String get defaultSystemProxyHost => - setting.get(SettingBoxKey.systemProxyHost, defaultValue: ''); - - static String get defaultSystemProxyPort => - setting.get(SettingBoxKey.systemProxyPort, defaultValue: ''); - - static int get defaultReplySort => - setting.get(SettingBoxKey.replySortType, defaultValue: 1); - - static int get defaultDynamicType => - setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0); - - static double get blockLimit => - setting.get(SettingBoxKey.blockLimit, defaultValue: 0.0); - - static double get refreshDragPercentage => - setting.get(SettingBoxKey.refreshDragPercentage, defaultValue: 0.25); - - static double get refreshDisplacement => - setting.get(SettingBoxKey.refreshDisplacement, defaultValue: 20.0); - - static String get blockUserID { - String blockUserID = - setting.get(SettingBoxKey.blockUserID, defaultValue: ''); - if (blockUserID.isEmpty) { - blockUserID = const Uuid().v4().replaceAll('-', ''); - setting.put(SettingBoxKey.blockUserID, blockUserID); - } - return blockUserID; - } - - static bool get blockToast => - setting.get(SettingBoxKey.blockToast, defaultValue: true); - - static String get blockServer => setting.get(SettingBoxKey.blockServer, - defaultValue: HttpString.sponsorBlockBaseUrl); - - static bool get blockTrack => - setting.get(SettingBoxKey.blockTrack, defaultValue: true); - - static bool get checkDynamic => - setting.get(SettingBoxKey.checkDynamic, defaultValue: true); - - static int get dynamicPeriod => - setting.get(SettingBoxKey.dynamicPeriod, defaultValue: 5); - - static int get schemeVariant => - setting.get(SettingBoxKey.schemeVariant, defaultValue: 10); - - static double get danmakuFontScaleFS => - setting.get(SettingBoxKey.danmakuFontScaleFS, defaultValue: 1.2); - - static bool get danmakuMassiveMode => - setting.get(SettingBoxKey.danmakuMassiveMode, defaultValue: false); - - static double get subtitleFontScale => - setting.get(SettingBoxKey.subtitleFontScale, defaultValue: 1.0); - - static double get subtitleFontScaleFS => - setting.get(SettingBoxKey.subtitleFontScaleFS, defaultValue: 1.5); - - static bool get showViewPoints => - setting.get(SettingBoxKey.showViewPoints, defaultValue: true); - - static bool get showRelatedVideo => - setting.get(SettingBoxKey.showRelatedVideo, defaultValue: true); - - static bool get showVideoReply => - setting.get(SettingBoxKey.showVideoReply, defaultValue: true); - - static bool get showBangumiReply => - setting.get(SettingBoxKey.showBangumiReply, defaultValue: true); - - static bool get alwaysExapndIntroPanel => - setting.get(SettingBoxKey.alwaysExapndIntroPanel, defaultValue: false); - - static bool get exapndIntroPanelH => - setting.get(SettingBoxKey.exapndIntroPanelH, defaultValue: false); - - static bool get horizontalSeasonPanel => - setting.get(SettingBoxKey.horizontalSeasonPanel, defaultValue: false); - - static bool get horizontalMemberPage => - setting.get(SettingBoxKey.horizontalMemberPage, defaultValue: false); - - static int get replyLengthLimit => - setting.get(SettingBoxKey.replyLengthLimit, defaultValue: 6); - - static int get defaultPicQa => - setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); - - static double get danmakuLineHeight => - setting.get(SettingBoxKey.danmakuLineHeight, defaultValue: 1.6); - - static bool get showArgueMsg => - setting.get(SettingBoxKey.showArgueMsg, defaultValue: true); - - static bool get reverseFromFirst => - setting.get(SettingBoxKey.reverseFromFirst, defaultValue: true); - - static int get subtitlePaddingH => - setting.get(SettingBoxKey.subtitlePaddingH, defaultValue: 24); - - static int get subtitlePaddingB => - setting.get(SettingBoxKey.subtitlePaddingB, defaultValue: 24); - - static double get subtitleBgOpaticy => - setting.get(SettingBoxKey.subtitleBgOpaticy, defaultValue: 0.67); - - static double get subtitleStrokeWidth => - setting.get(SettingBoxKey.subtitleStrokeWidth, defaultValue: 2.0); - - static int get subtitleFontWeight => - setting.get(SettingBoxKey.subtitleFontWeight, defaultValue: 5); - - static bool get badCertificateCallback => - setting.get(SettingBoxKey.badCertificateCallback, defaultValue: false); - - static bool get continuePlayingPart => - setting.get(SettingBoxKey.continuePlayingPart, defaultValue: true); - - static bool get cdnSpeedTest => - setting.get(SettingBoxKey.cdnSpeedTest, defaultValue: true); - - static bool get autoUpdate => - setting.get(SettingBoxKey.autoUpdate, defaultValue: true); - - static bool get horizontalPreview => - setting.get(SettingBoxKey.horizontalPreview, defaultValue: false); - - static bool get openInBrowser => - setting.get(SettingBoxKey.openInBrowser, defaultValue: false); - - static bool get savedRcmdTip => - setting.get(SettingBoxKey.savedRcmdTip, defaultValue: true); - - static bool get showVipDanmaku => - setting.get(SettingBoxKey.showVipDanmaku, defaultValue: true); - - static bool get showSpecialDanmaku => - setting.get(SettingBoxKey.showSpecialDanmaku, defaultValue: false); - - static bool get mergeDanmaku => - setting.get(SettingBoxKey.mergeDanmaku, defaultValue: false); - - static bool get showHotRcmd => - setting.get(SettingBoxKey.showHotRcmd, defaultValue: false); - - static String get audioNormalization => - setting.get(SettingBoxKey.audioNormalization, defaultValue: '0'); - - static int get superResolutionType => - setting.get(SettingBoxKey.superResolutionType, defaultValue: 0); - - static bool get preInitPlayer => - setting.get(SettingBoxKey.preInitPlayer, defaultValue: false); - - static bool get mainTabBarView => - setting.get(SettingBoxKey.mainTabBarView, defaultValue: false); - - static bool get searchSuggestion => - setting.get(SettingBoxKey.searchSuggestion, defaultValue: true); - - static bool get showDynDecorate => - setting.get(SettingBoxKey.showDynDecorate, defaultValue: true); - - static bool get enableLivePhoto => - setting.get(SettingBoxKey.enableLivePhoto, defaultValue: true); - - static bool get showSeekPreview => - setting.get(SettingBoxKey.showSeekPreview, defaultValue: true); - - static bool get showDmChart => - setting.get(SettingBoxKey.showDmChart, defaultValue: false); - - static bool get enableCommAntifraud => - setting.get(SettingBoxKey.enableCommAntifraud, defaultValue: false); - - static bool get biliSendCommAntifraud => - setting.get(SettingBoxKey.biliSendCommAntifraud, defaultValue: false); - - static bool get enableCreateDynAntifraud => - setting.get(SettingBoxKey.enableCreateDynAntifraud, defaultValue: false); - - static bool get coinWithLike => - setting.get(SettingBoxKey.coinWithLike, defaultValue: false); - - static bool get isPureBlackTheme => - setting.get(SettingBoxKey.isPureBlackTheme, defaultValue: false); - - static bool antiGoodsDyn = - setting.get(SettingBoxKey.antiGoodsDyn, defaultValue: false); - - static bool get antiGoodsReply => - setting.get(SettingBoxKey.antiGoodsReply, defaultValue: false); - - static bool get expandDynLivePanel => - setting.get(SettingBoxKey.expandDynLivePanel, defaultValue: false); - - static bool slideDismissReplyPage = setting - .get(SettingBoxKey.slideDismissReplyPage, defaultValue: Platform.isIOS); - - static bool get showFSActionItem => - setting.get(SettingBoxKey.showFSActionItem, defaultValue: true); - - static bool get enableShrinkVideoSize => - setting.get(SettingBoxKey.enableShrinkVideoSize, defaultValue: true); - - static bool get showDynActionBar => - setting.get(SettingBoxKey.showDynActionBar, defaultValue: true); - - static bool get darkVideoPage => - setting.get(SettingBoxKey.darkVideoPage, defaultValue: false); - - static bool get enableSlideVolumeBrightness => setting - .get(SettingBoxKey.enableSlideVolumeBrightness, defaultValue: true); - - static bool get enableSlideFS => - setting.get(SettingBoxKey.enableSlideFS, defaultValue: true); - - static int get retryCount => - setting.get(SettingBoxKey.retryCount, defaultValue: 2); - - static int get retryDelay => - setting.get(SettingBoxKey.retryDelay, defaultValue: 500); - - static int get liveQuality => setting.get(SettingBoxKey.liveQuality, - defaultValue: LiveQuality.origin.code); - - static int get liveQualityCellular => - setting.get(SettingBoxKey.liveQualityCellular, - defaultValue: LiveQuality.superHD.code); - - static int get appFontWeight => - setting.get(SettingBoxKey.appFontWeight, defaultValue: -1); - - static bool get enableDragSubtitle => - setting.get(SettingBoxKey.enableDragSubtitle, defaultValue: false); - - static int get fastForBackwardDuration => - setting.get(SettingBoxKey.fastForBackwardDuration, defaultValue: 10); - - static bool get recordSearchHistory => - setting.get(SettingBoxKey.recordSearchHistory, defaultValue: true); - - static bool get navSearchStreamDebounce => - setting.get(SettingBoxKey.navSearchStreamDebounce, defaultValue: false); - - static String get webdavUri => - setting.get(SettingBoxKey.webdavUri, defaultValue: ''); - - static String get webdavUsername => - setting.get(SettingBoxKey.webdavUsername, defaultValue: ''); - - static String get webdavPassword => - setting.get(SettingBoxKey.webdavPassword, defaultValue: ''); - - static String get webdavDirectory => - setting.get(SettingBoxKey.webdavDirectory, defaultValue: '/'); - - static bool get showPgcTimeline => - setting.get(SettingBoxKey.showPgcTimeline, defaultValue: true); - - static Transition pageTransition = Transition.values[setting.get( - SettingBoxKey.pageTransition, - defaultValue: Transition.native.index)]; - - static num get maxCacheSize => - setting.get(SettingBoxKey.maxCacheSize) ?? pow(1024, 3); - - static bool get optTabletNav => - setting.get(SettingBoxKey.optTabletNav, defaultValue: true); - - static bool get horizontalScreen { - return setting.get(SettingBoxKey.horizontalScreen) ?? isTablet; - } - - static bool get isTablet { - bool isTablet; - if (Get.context != null) { - isTablet = Get.context!.isTablet; - } else { - final view = WidgetsBinding.instance.platformDispatcher.views.first; - final screenSize = view.physicalSize / view.devicePixelRatio; - final shortestSide = min(screenSize.width.abs(), screenSize.height.abs()); - isTablet = shortestSide >= 600; - } - return isTablet; - } - - static String get banWordForDyn => - GStorage.setting.get(SettingBoxKey.banWordForDyn, defaultValue: ''); - - static bool get enableLog => - setting.get(SettingBoxKey.enableLog, defaultValue: true); - - static List get dynamicDetailRatio => List.from(setting - .get(SettingBoxKey.dynamicDetailRatio, defaultValue: const [60.0, 40.0])); - - static Set get blackMids => - localCache.get(LocalCacheKey.blackMids, defaultValue: const {}); - - static set blackMids(Set blackMidsSet) { - localCache.put(LocalCacheKey.blackMids, blackMidsSet); - } - - static RuleFilter get danmakuFilterRule => localCache - .get(LocalCacheKey.danmakuFilterRules, defaultValue: RuleFilter.empty()); - - static void setBlackMid(int mid) { - localCache.put(LocalCacheKey.blackMids, blackMids..add(mid)); - } - - static void removeBlackMid(int mid) { - localCache.put(LocalCacheKey.blackMids, blackMids..remove(mid)); - } - - static MemberTabType get memberTab => MemberTabType - .values[setting.get(SettingBoxKey.memberTab, defaultValue: 0)]; - - static int get themeTypeInt => - setting.get(SettingBoxKey.themeMode, defaultValue: ThemeType.system.code); - - static ThemeMode get themeMode { - return switch (themeTypeInt) { - 0 => ThemeMode.light, - 1 => ThemeMode.dark, - _ => ThemeMode.system - }; - } - - // static final SpringDescription _kDefaultSpring = SpringDescription.withDampingRatio( - // mass: 0.5, - // stiffness: 100.0, - // ratio: 1.1, - // ); - // damping = ratio * 2.0 * math.sqrt(mass * stiffness) - static final List springDescription = List.from( - setting.get(SettingBoxKey.springDescription) ?? - [0.5, 100.0, 2.2 * sqrt(50)], // [mass, stiffness, damping] - ); - - // static Brightness get brightness { - // return switch (_themeMode) { - // 0 => Brightness.light, - // 1 => Brightness.dark, - // _ => PlatformDispatcher.instance.platformBrightness - // }; - // } - static Future init() async { final Directory dir = await getApplicationSupportDirectory(); final String path = dir.path; @@ -584,15 +49,8 @@ class GStorage { ); // 视频设置 video = await Hive.openBox('video'); - displacement = refreshDisplacement; - kDragContainerExtentPercentage = refreshDragPercentage; await Accounts.init(); - - // 设置全局变量 - GlobalData() - ..imgQuality = defaultPicQa - ..replyLengthLimit = replyLengthLimit; } static String exportAllSettings() { @@ -643,362 +101,3 @@ class GStorage { Accounts.close(); } } - -class SettingBoxKey { - /// 播放器 - static const String btmProgressBehavior = 'btmProgressBehavior', - defaultVideoSpeed = 'defaultVideoSpeed', - autoUpgradeEnable = 'autoUpgradeEnable', - feedBackEnable = 'feedBackEnable', - defaultVideoQa = 'defaultVideoQa', - defaultVideoQaCellular = 'defaultVideoQaCellular', - defaultAudioQa = 'defaultAudioQa', - defaultAudioQaCellular = 'defaultAudioQaCellular', - autoPlayEnable = 'autoPlayEnable', - fullScreenMode = 'fullScreenMode', - defaultDecode = 'defaultDecode', - secondDecode = 'secondDecode', - danmakuEnable = 'danmakuEnable', - defaultToastOp = 'defaultToastOp', - defaultPicQa = 'defaultPicQa', - enableHA = 'enableHA', - useOpenSLES = 'useOpenSLES', - expandBuffer = 'expandBuffer', - hardwareDecoding = 'hardwareDecoding', - videoSync = 'videoSync', - enableVerticalExpand = 'enableVerticalExpand', - enableOnlineTotal = 'enableOnlineTotal', - enableAutoBrightness = 'enableAutoBrightness', - enableAutoEnter = 'enableAutoEnter', - enableAutoExit = 'enableAutoExit', - enableLongShowControl = 'enableLongShowControl', - allowRotateScreen = 'allowRotateScreen', - horizontalScreen = 'horizontalScreen', - p1080 = 'p1080', - // ignore: constant_identifier_names - CDNService = 'CDNService', - disableAudioCDN = 'disableAudioCDN', - autoPiP = 'autoPiP', - pipNoDanmaku = 'pipNoDanmaku', - enableAutoLongPressSpeed = 'enableAutoLongPressSpeed', - subtitlePreference = 'subtitlePreference', - useRelativeSlide = 'useRelativeSlide', - sliderDuration = 'sliderOffset', - - // youtube 双击快进快退 - enableQuickDouble = 'enableQuickDouble', - fullScreenGestureReverse = 'fullScreenGestureReverse', - enableShowDanmaku = 'enableShowDanmaku', - enableBackgroundPlay = 'enableBackgroundPlay', - continuePlayInBackground = 'continuePlayInBackground', - - /// 隐私 - // anonymity = 'anonymity', - - /// 推荐 - enableRcmdDynamic = 'enableRcmdDynamic', - appRcmd = 'appRcmd', - enableSaveLastData = 'enableSaveLastData', - minDurationForRcmd = 'minDurationForRcmd', - minPlayForRcmd = 'minPlayForRcmd', - minLikeRatioForRecommend = 'minLikeRatioForRecommend', - exemptFilterForFollowed = 'exemptFilterForFollowed', - banWordForRecommend = 'banWordForRecommend', - applyFilterToRelatedVideos = 'applyFilterToRelatedVideos', - - /// 其他 - autoUpdate = 'autoUpdate', - autoClearCache = 'autoClearCache', - maxCacheSize = 'maxCacheSize', - defaultShowComment = 'defaultShowComment', - replySortType = 'replySortType', - defaultDynamicType = 'defaultDynamicType', - enableHotKey = 'enableHotKey', - enableSearchRcmd = 'enableSearchRcmd', - enableQuickFav = 'enableQuickFav', - enableWordRe = 'enableWordRe', - enableSearchWord = 'enableSearchWord', - enableSystemProxy = 'enableSystemProxy', - enableAi = 'enableAi', - disableLikeMsg = 'disableLikeMsg', - defaultHomePage = 'defaultHomePage', - previewQuality = 'previewQuality', - checkDynamic = 'checkDynamic', - dynamicPeriod = 'dynamicPeriod', - schemeVariant = 'schemeVariant', - showViewPoints = 'showViewPoints', - showRelatedVideo = 'showRelatedVideo', - showVideoReply = 'showVideoReply', - showBangumiReply = 'showBangumiReply', - alwaysExapndIntroPanel = 'alwaysExapndIntroPanel', - exapndIntroPanelH = 'exapndIntroPanelH', - horizontalSeasonPanel = 'horizontalSeasonPanel', - horizontalMemberPage = 'horizontalMemberPage', - replyLengthLimit = 'replyLengthLimit', - showArgueMsg = 'showArgueMsg', - reverseFromFirst = 'reverseFromFirst', - subtitlePaddingH = 'subtitlePaddingH', - subtitlePaddingB = 'subtitlePaddingB', - subtitleBgOpaticy = 'subtitleBgOpaticy', - subtitleStrokeWidth = 'subtitleStrokeWidth', - subtitleFontScale = 'subtitleFontScale', - subtitleFontScaleFS = 'subtitleFontScaleFS', - subtitleFontWeight = 'subtitleFontWeight', - badCertificateCallback = 'badCertificateCallback', - continuePlayingPart = 'continuePlayingPart', - cdnSpeedTest = 'cdnSpeedTest', - horizontalPreview = 'horizontalPreview', - banWordForReply = 'banWordForReply', - banWordForZone = 'banWordForZone', - savedRcmdTip = 'savedRcmdTip', - openInBrowser = 'openInBrowser', - refreshDragPercentage = 'refreshDragPercentage', - refreshDisplacement = 'refreshDisplacement', - showVipDanmaku = 'showVipDanmaku', - showSpecialDanmaku = 'showSpecialDanmaku', - mergeDanmaku = 'mergeDanmaku', - showHotRcmd = 'showHotRcmd', - audioNormalization = 'audioNormalization', - superResolutionType = 'superResolutionType', - preInitPlayer = 'preInitPlayer', - mainTabBarView = 'mainTabBarView', - searchSuggestion = 'searchSuggestion', - showDynDecorate = 'showDynDecorate', - enableLivePhoto = 'enableLivePhoto', - showSeekPreview = 'showSeekPreview', - showDmChart = 'showDmChart', - enableCommAntifraud = 'enableCommAntifraud', - biliSendCommAntifraud = 'biliSendCommAntifraud', - enableCreateDynAntifraud = 'enableCreateDynAntifraud', - coinWithLike = 'coinWithLike', - isPureBlackTheme = 'isPureBlackTheme', - antiGoodsDyn = 'antiGoodsDyn', - antiGoodsReply = 'antiGoodsReply', - expandDynLivePanel = 'expandDynLivePanel', - springDescription = 'springDescription', - enableHttp2 = 'enableHttp2', - slideDismissReplyPage = 'slideDismissReplyPage', - showFSActionItem = 'showFSActionItem', - enableShrinkVideoSize = 'enableShrinkVideoSize', - showDynActionBar = 'showDynActionBar', - darkVideoPage = 'darkVideoPage', - enableSlideVolumeBrightness = 'enableSlideVolumeBrightness', - enableSlideFS = 'enableSlideFS', - retryCount = 'retryCount', - retryDelay = 'retryDelay', - liveQuality = 'liveQuality', - liveQualityCellular = 'liveQualityCellular', - appFontWeight = 'appFontWeight', - enableDragSubtitle = 'enableDragSubtitle', - fastForBackwardDuration = 'fastForBackwardDuration', - recordSearchHistory = 'recordSearchHistory', - navSearchStreamDebounce = 'navSearchStreamDebounce', - showPgcTimeline = 'showPgcTimeline', - pageTransition = 'pageTransition', - optTabletNav = 'optTabletNav', - banWordForDyn = 'banWordForDyn', - enableLog = 'enableLog', - - // WebDAV - webdavUri = 'webdavUri', - webdavUsername = 'webdavUsername', - webdavPassword = 'webdavPassword', - webdavDirectory = 'webdavDirectory', - - // Sponsor Block - enableSponsorBlock = 'enableSponsorBlock', - blockSettings = 'blockSettings', - blockLimit = 'blockLimit', - blockColor = 'blockColor', - blockUserID = 'blockUserID', - blockToast = 'blockToast', - blockServer = 'blockServer', - blockTrack = 'blockTrack', - - // 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 字体粗细 - danmakuWeight = 'danmakuWeight', - danmakuBlockType = 'danmakuBlockType', - danmakuShowArea = 'danmakuShowArea', - danmakuOpacity = 'danmakuOpacity', - danmakuFontScale = 'danmakuFontScale', - danmakuFontScaleFS = 'danmakuFontScaleFS', - danmakuDuration = 'danmakuDuration', - danmakuStaticDuration = 'danmakuStaticDuration', - danmakuMassiveMode = 'danmakuMassiveMode', - danmakuLineHeight = 'danmakuLineHeight', - strokeWidth = 'strokeWidth', - fontWeight = 'fontWeight', - memberTab = 'memberTab', - dynamicDetailRatio = 'dynamicDetailRatio', - - // 代理host port - systemProxyHost = 'systemProxyHost', - systemProxyPort = 'systemProxyPort'; - - /// 外观 - static const String themeMode = 'themeMode', - defaultTextScale = 'textScale', - dynamicColor = 'dynamicColor', // bool - customColor = 'customColor', // 自定义主题色 - enableSingleRow = 'enableSingleRow', // 首页单列 - displayMode = 'displayMode', - smallCardWidth = 'smallCardWidth', - videoPlayerRemoveSafeArea = 'videoPlayerRemoveSafeArea', // 视频播放器移除安全边距 - dynamicsWaterfallFlow = 'dynamicsWaterfallFlow', // 动态瀑布流 - upPanelPosition = 'upPanelPosition', // up主面板位置 - dynamicsShowAllFollowedUp = 'dynamicsShowAllFollowedUp', // 动态显示全部关注up - useSideBar = 'useSideBar', - enableMYBar = 'enableMYBar', - hideSearchBar = 'hideSearchBar', // 收起顶栏 - hideTabBar = 'hideTabBar', // 收起底栏 - tabBarSort = 'tabBarSort', // 首页tabbar - dynamicBadgeMode = 'dynamicBadgeMode', - msgBadgeMode = 'msgBadgeMode', - msgUnReadTypeV2 = 'msgUnReadTypeV2', - hiddenSettingUnlocked = 'hiddenSettingUnlocked', - enableGradientBg = 'enableGradientBg', - navBarSort = 'navBarSort'; -} - -class LocalCacheKey { - // 历史记录暂停状态 默认false 记录 - static const String historyPause = 'historyPause', - - // 隐私设置-黑名单管理 - blackMids = 'blackMids', - // 弹幕屏蔽规则 - danmakuFilterRules = 'danmakuFilterRules', - // // access_key - // accessKey = 'accessKey', - - // - mixinKey = 'mixinKey', - timeStamp = 'timeStamp'; -} - -class VideoBoxKey { - // 视频比例 - static const String videoFit = 'videoFit', - // 亮度 - videoBrightness = 'videoBrightness', - // 倍速 - videoSpeed = 'videoSpeed', - // 播放顺序 - playRepeat = 'playRepeat', - // 默认倍速 - playSpeedDefault = 'playSpeedDefault', - // 默认长按倍速 - longPressSpeedDefault = 'longPressSpeedDefault', - // 倍速集合 - speedsList = 'speedsList', - // 画面填充比例 - cacheVideoFit = 'cacheVideoFit'; -} - -class Accounts { - static late final Box account; - static final Map accountMode = {}; - static Account get main => accountMode[AccountType.main]!; - // static set main(Account account) => set(AccountType.main, account); - - static Future init() async { - account = await Hive.openBox('account', - compactionStrategy: (int entries, int deletedEntries) { - return deletedEntries > 2; - }); - await _migrate(); - } - - static Future _migrate() async { - final Directory tempDir = await getApplicationSupportDirectory(); - final String tempPath = "${tempDir.path}/.plpl/"; - final Directory dir = Directory(tempPath); - if (dir.existsSync()) { - if (kDebugMode) debugPrint('migrating...'); - final cookieJar = - PersistCookieJar(ignoreExpires: true, storage: FileStorage(tempPath)); - await cookieJar.forceInit(); - final cookies = DefaultCookieJar(ignoreExpires: true) - ..domainCookies.addAll(cookieJar.domainCookies); - final localAccessKey = - GStorage.localCache.get('accessKey', defaultValue: {}); - - final isLogin = - cookies.domainCookies['bilibili.com']?['/']?['SESSDATA'] != null; - - await Future.wait([ - GStorage.localCache.delete('accessKey'), - GStorage.localCache.delete('danmakuFilterRule'), - GStorage.localCache.delete('blackMidsList'), - dir.delete(recursive: true), - if (isLogin) - LoginAccount(cookies, localAccessKey['value'], - localAccessKey['refresh'], AccountType.values.toSet()) - .onChange() - ]); - if (kDebugMode) debugPrint('migrated successfully'); - } - } - - static void refresh() { - for (var a in account.values) { - for (var t in a.type) { - accountMode[t] = a; - } - } - for (var type in AccountType.values) { - accountMode[type] ??= AnonymousAccount(); - } - // await Future.wait((accountMode.values.toSet() - // ..retainWhere((i) => !i.activited)) - // .map((i) => Request.buvidActive(i))); - } - - static Future clear() async { - await account.clear(); - for (var i in AccountType.values) { - accountMode[i] = AnonymousAccount(); - } - await AnonymousAccount().delete(); - // Request.buvidActive(AnonymousAccount()); - } - - static void close() { - account - ..compact() - ..close(); - } - - static Future deleteAll(Set accounts) async { - var isloginMain = Accounts.main.isLogin; - Accounts.accountMode - .updateAll((_, a) => accounts.contains(a) ? AnonymousAccount() : a); - await Future.wait(accounts.map((i) => i.delete())); - if (isloginMain && !Accounts.main.isLogin) { - await LoginUtils.onLogoutMain(); - } - } - - static Future set(AccountType key, Account account) async { - await (accountMode[key]?..type.remove(key))?.onChange(); - accountMode[key] = account..type.add(key); - await account.onChange(); - // if (!account.activited) await Request.buvidActive(account); - switch (key) { - case AccountType.main: - await (account.isLogin - ? LoginUtils.onLoginMain() - : LoginUtils.onLogoutMain()); - break; - case AccountType.heartbeat: - MineController.anonymity.value = !account.isLogin; - break; - default: - break; - } - } - - static Account get(AccountType key) { - return accountMode[key]!; - } -} diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart new file mode 100644 index 000000000..150f7976b --- /dev/null +++ b/lib/utils/storage_key.dart @@ -0,0 +1,250 @@ +class SettingBoxKey { + /// 播放器 + static const String btmProgressBehavior = 'btmProgressBehavior', + defaultVideoSpeed = 'defaultVideoSpeed', + autoUpgradeEnable = 'autoUpgradeEnable', + feedBackEnable = 'feedBackEnable', + defaultVideoQa = 'defaultVideoQa', + defaultVideoQaCellular = 'defaultVideoQaCellular', + defaultAudioQa = 'defaultAudioQa', + defaultAudioQaCellular = 'defaultAudioQaCellular', + autoPlayEnable = 'autoPlayEnable', + fullScreenMode = 'fullScreenMode', + defaultDecode = 'defaultDecode', + secondDecode = 'secondDecode', + danmakuEnable = 'danmakuEnable', + defaultToastOp = 'defaultToastOp', + defaultPicQa = 'defaultPicQa', + enableHA = 'enableHA', + useOpenSLES = 'useOpenSLES', + expandBuffer = 'expandBuffer', + hardwareDecoding = 'hardwareDecoding', + videoSync = 'videoSync', + enableVerticalExpand = 'enableVerticalExpand', + enableOnlineTotal = 'enableOnlineTotal', + enableAutoBrightness = 'enableAutoBrightness', + enableAutoEnter = 'enableAutoEnter', + enableAutoExit = 'enableAutoExit', + enableLongShowControl = 'enableLongShowControl', + allowRotateScreen = 'allowRotateScreen', + horizontalScreen = 'horizontalScreen', + p1080 = 'p1080', + // ignore: constant_identifier_names + CDNService = 'CDNService', + disableAudioCDN = 'disableAudioCDN', + autoPiP = 'autoPiP', + pipNoDanmaku = 'pipNoDanmaku', + enableAutoLongPressSpeed = 'enableAutoLongPressSpeed', + subtitlePreference = 'subtitlePreference', + useRelativeSlide = 'useRelativeSlide', + sliderDuration = 'sliderOffset', + + // youtube 双击快进快退 + enableQuickDouble = 'enableQuickDouble', + fullScreenGestureReverse = 'fullScreenGestureReverse', + enableShowDanmaku = 'enableShowDanmaku', + enableBackgroundPlay = 'enableBackgroundPlay', + continuePlayInBackground = 'continuePlayInBackground', + + /// 隐私 + // anonymity = 'anonymity', + + /// 推荐 + enableRcmdDynamic = 'enableRcmdDynamic', + appRcmd = 'appRcmd', + enableSaveLastData = 'enableSaveLastData', + minDurationForRcmd = 'minDurationForRcmd', + minPlayForRcmd = 'minPlayForRcmd', + minLikeRatioForRecommend = 'minLikeRatioForRecommend', + exemptFilterForFollowed = 'exemptFilterForFollowed', + banWordForRecommend = 'banWordForRecommend', + applyFilterToRelatedVideos = 'applyFilterToRelatedVideos', + + /// 其他 + autoUpdate = 'autoUpdate', + autoClearCache = 'autoClearCache', + maxCacheSize = 'maxCacheSize', + defaultShowComment = 'defaultShowComment', + replySortType = 'replySortType', + defaultDynamicType = 'defaultDynamicType', + enableHotKey = 'enableHotKey', + enableSearchRcmd = 'enableSearchRcmd', + enableQuickFav = 'enableQuickFav', + enableWordRe = 'enableWordRe', + enableSearchWord = 'enableSearchWord', + enableSystemProxy = 'enableSystemProxy', + enableAi = 'enableAi', + disableLikeMsg = 'disableLikeMsg', + defaultHomePage = 'defaultHomePage', + previewQuality = 'previewQuality', + checkDynamic = 'checkDynamic', + dynamicPeriod = 'dynamicPeriod', + schemeVariant = 'schemeVariant', + showViewPoints = 'showViewPoints', + showRelatedVideo = 'showRelatedVideo', + showVideoReply = 'showVideoReply', + showBangumiReply = 'showBangumiReply', + alwaysExapndIntroPanel = 'alwaysExapndIntroPanel', + exapndIntroPanelH = 'exapndIntroPanelH', + horizontalSeasonPanel = 'horizontalSeasonPanel', + horizontalMemberPage = 'horizontalMemberPage', + replyLengthLimit = 'replyLengthLimit', + showArgueMsg = 'showArgueMsg', + reverseFromFirst = 'reverseFromFirst', + subtitlePaddingH = 'subtitlePaddingH', + subtitlePaddingB = 'subtitlePaddingB', + subtitleBgOpaticy = 'subtitleBgOpaticy', + subtitleStrokeWidth = 'subtitleStrokeWidth', + subtitleFontScale = 'subtitleFontScale', + subtitleFontScaleFS = 'subtitleFontScaleFS', + subtitleFontWeight = 'subtitleFontWeight', + badCertificateCallback = 'badCertificateCallback', + continuePlayingPart = 'continuePlayingPart', + cdnSpeedTest = 'cdnSpeedTest', + horizontalPreview = 'horizontalPreview', + banWordForReply = 'banWordForReply', + banWordForZone = 'banWordForZone', + savedRcmdTip = 'savedRcmdTip', + openInBrowser = 'openInBrowser', + refreshDragPercentage = 'refreshDragPercentage', + refreshDisplacement = 'refreshDisplacement', + showVipDanmaku = 'showVipDanmaku', + showSpecialDanmaku = 'showSpecialDanmaku', + mergeDanmaku = 'mergeDanmaku', + showHotRcmd = 'showHotRcmd', + audioNormalization = 'audioNormalization', + superResolutionType = 'superResolutionType', + preInitPlayer = 'preInitPlayer', + mainTabBarView = 'mainTabBarView', + searchSuggestion = 'searchSuggestion', + showDynDecorate = 'showDynDecorate', + enableLivePhoto = 'enableLivePhoto', + showSeekPreview = 'showSeekPreview', + showDmChart = 'showDmChart', + enableCommAntifraud = 'enableCommAntifraud', + biliSendCommAntifraud = 'biliSendCommAntifraud', + enableCreateDynAntifraud = 'enableCreateDynAntifraud', + coinWithLike = 'coinWithLike', + isPureBlackTheme = 'isPureBlackTheme', + antiGoodsDyn = 'antiGoodsDyn', + antiGoodsReply = 'antiGoodsReply', + expandDynLivePanel = 'expandDynLivePanel', + springDescription = 'springDescription', + enableHttp2 = 'enableHttp2', + slideDismissReplyPage = 'slideDismissReplyPage', + showFSActionItem = 'showFSActionItem', + enableShrinkVideoSize = 'enableShrinkVideoSize', + showDynActionBar = 'showDynActionBar', + darkVideoPage = 'darkVideoPage', + enableSlideVolumeBrightness = 'enableSlideVolumeBrightness', + enableSlideFS = 'enableSlideFS', + retryCount = 'retryCount', + retryDelay = 'retryDelay', + liveQuality = 'liveQuality', + liveQualityCellular = 'liveQualityCellular', + appFontWeight = 'appFontWeight', + enableDragSubtitle = 'enableDragSubtitle', + fastForBackwardDuration = 'fastForBackwardDuration', + recordSearchHistory = 'recordSearchHistory', + navSearchStreamDebounce = 'navSearchStreamDebounce', + showPgcTimeline = 'showPgcTimeline', + pageTransition = 'pageTransition', + optTabletNav = 'optTabletNav', + banWordForDyn = 'banWordForDyn', + enableLog = 'enableLog', + + // WebDAV + webdavUri = 'webdavUri', + webdavUsername = 'webdavUsername', + webdavPassword = 'webdavPassword', + webdavDirectory = 'webdavDirectory', + + // Sponsor Block + enableSponsorBlock = 'enableSponsorBlock', + blockSettings = 'blockSettings', + blockLimit = 'blockLimit', + blockColor = 'blockColor', + blockUserID = 'blockUserID', + blockToast = 'blockToast', + blockServer = 'blockServer', + blockTrack = 'blockTrack', + + // 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 字体粗细 + danmakuWeight = 'danmakuWeight', + danmakuBlockType = 'danmakuBlockType', + danmakuShowArea = 'danmakuShowArea', + danmakuOpacity = 'danmakuOpacity', + danmakuFontScale = 'danmakuFontScale', + danmakuFontScaleFS = 'danmakuFontScaleFS', + danmakuDuration = 'danmakuDuration', + danmakuStaticDuration = 'danmakuStaticDuration', + danmakuMassiveMode = 'danmakuMassiveMode', + danmakuLineHeight = 'danmakuLineHeight', + strokeWidth = 'strokeWidth', + fontWeight = 'fontWeight', + memberTab = 'memberTab', + dynamicDetailRatio = 'dynamicDetailRatio', + + // 代理host port + systemProxyHost = 'systemProxyHost', + systemProxyPort = 'systemProxyPort'; + + /// 外观 + static const String themeMode = 'themeMode', + defaultTextScale = 'textScale', + dynamicColor = 'dynamicColor', // bool + customColor = 'customColor', // 自定义主题色 + enableSingleRow = 'enableSingleRow', // 首页单列 + displayMode = 'displayMode', + smallCardWidth = 'smallCardWidth', + videoPlayerRemoveSafeArea = 'videoPlayerRemoveSafeArea', // 视频播放器移除安全边距 + dynamicsWaterfallFlow = 'dynamicsWaterfallFlow', // 动态瀑布流 + upPanelPosition = 'upPanelPosition', // up主面板位置 + dynamicsShowAllFollowedUp = 'dynamicsShowAllFollowedUp', // 动态显示全部关注up + useSideBar = 'useSideBar', + enableMYBar = 'enableMYBar', + hideSearchBar = 'hideSearchBar', // 收起顶栏 + hideTabBar = 'hideTabBar', // 收起底栏 + tabBarSort = 'tabBarSort', // 首页tabbar + dynamicBadgeMode = 'dynamicBadgeMode', + msgBadgeMode = 'msgBadgeMode', + msgUnReadTypeV2 = 'msgUnReadTypeV2', + hiddenSettingUnlocked = 'hiddenSettingUnlocked', + enableGradientBg = 'enableGradientBg', + navBarSort = 'navBarSort'; +} + +class LocalCacheKey { + // 历史记录暂停状态 默认false 记录 + static const String historyPause = 'historyPause', + + // 隐私设置-黑名单管理 + blackMids = 'blackMids', + // 弹幕屏蔽规则 + danmakuFilterRules = 'danmakuFilterRules', + // // access_key + // accessKey = 'accessKey', + + // + mixinKey = 'mixinKey', + timeStamp = 'timeStamp'; +} + +class VideoBoxKey { + // 视频比例 + static const String videoFit = 'videoFit', + // 亮度 + videoBrightness = 'videoBrightness', + // 倍速 + videoSpeed = 'videoSpeed', + // 播放顺序 + playRepeat = 'playRepeat', + // 默认倍速 + playSpeedDefault = 'playSpeedDefault', + // 默认长按倍速 + longPressSpeedDefault = 'longPressSpeedDefault', + // 倍速集合 + speedsList = 'speedsList', + // 画面填充比例 + cacheVideoFit = 'cacheVideoFit'; +} diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart new file mode 100644 index 000000000..0c19e2d3c --- /dev/null +++ b/lib/utils/storage_pref.dart @@ -0,0 +1,717 @@ +import 'dart:io'; +import 'dart:math' show pow, min, sqrt; + +import 'package:PiliPlus/common/widgets/pair.dart'; +import 'package:PiliPlus/http/constants.dart'; +import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart'; +import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; +import 'package:PiliPlus/models/common/home_tab_type.dart'; +import 'package:PiliPlus/models/common/member/tab_type.dart'; +import 'package:PiliPlus/models/common/msg/msg_unread_type.dart'; +import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart'; +import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart'; +import 'package:PiliPlus/models/common/theme/theme_type.dart'; +import 'package:PiliPlus/models/common/video/audio_quality.dart'; +import 'package:PiliPlus/models/common/video/cdn_type.dart'; +import 'package:PiliPlus/models/common/video/live_quality.dart'; +import 'package:PiliPlus/models/common/video/subtitle_pref_type.dart'; +import 'package:PiliPlus/models/common/video/video_decode_type.dart'; +import 'package:PiliPlus/models/common/video/video_quality.dart'; +import 'package:PiliPlus/models/user/danmaku_rule.dart'; +import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart'; +import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart'; +import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; +import 'package:PiliPlus/utils/global_data.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:uuid/uuid.dart'; + +class Pref { + static final Box _setting = GStorage.setting; + static final Box _video = GStorage.video; + static final Box _localCache = GStorage.localCache; + + static List get dynamicDetailRatio => List.from(_setting + .get(SettingBoxKey.dynamicDetailRatio, defaultValue: const [60.0, 40.0])); + + static Set get blackMids => + _localCache.get(LocalCacheKey.blackMids, defaultValue: const {}); + + static set blackMids(Set blackMidsSet) { + _localCache.put(LocalCacheKey.blackMids, blackMidsSet); + } + + static RuleFilter get danmakuFilterRule => _localCache + .get(LocalCacheKey.danmakuFilterRules, defaultValue: RuleFilter.empty()); + + static void setBlackMid(int mid) { + _localCache.put(LocalCacheKey.blackMids, GlobalData().blackMids..add(mid)); + } + + static void removeBlackMid(int mid) { + _localCache.put( + LocalCacheKey.blackMids, GlobalData().blackMids..remove(mid)); + } + + static MemberTabType get memberTab => MemberTabType + .values[_setting.get(SettingBoxKey.memberTab, defaultValue: 0)]; + + static int get themeTypeInt => _setting.get(SettingBoxKey.themeMode, + defaultValue: ThemeType.system.index); + + static ThemeMode get themeMode { + return switch (themeTypeInt) { + 0 => ThemeMode.light, + 1 => ThemeMode.dark, + _ => ThemeMode.system + }; + } + + static List get springDescription => List.from( + _setting.get(SettingBoxKey.springDescription) ?? + [0.5, 100.0, 2.2 * sqrt(50)], // [mass, stiffness, damping] + ); + + static List get speedList => List.from( + _video.get( + VideoBoxKey.speedsList, + defaultValue: const [0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 3.0], + ), + ); + + static List get tabbarSort => + List.from(_setting.get(SettingBoxKey.tabBarSort) ?? + HomeTabType.values.map((item) => item.index).toList()); + + static List> get blockSettings { + List list = List.from(_setting.get(SettingBoxKey.blockSettings) ?? + List.generate(SegmentType.values.length, (_) => 1)); + return SegmentType.values + .map((item) => Pair( + first: item, + second: SkipType.values[list[item.index]], + )) + .toList(); + } + + static List get blockColor { + List list = List.from( + _setting.get(SettingBoxKey.blockColor) ?? + List.generate(SegmentType.values.length, (_) => '')); + return SegmentType.values + .map((item) => list[item.index].isNotEmpty + ? Color( + int.tryParse('FF${list[item.index]}', radix: 16) ?? 0xFF000000) + : item.color) + .toList(); + } + + static bool get hiddenSettingUnlocked => + _setting.get(SettingBoxKey.hiddenSettingUnlocked, defaultValue: false); + + static bool get feedBackEnable => + _setting.get(SettingBoxKey.feedBackEnable, defaultValue: false); + + static int get picQuality => + _setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); + + static ThemeType get themeType => ThemeType.values[themeTypeInt]; + + static DynamicBadgeMode get dynamicBadgeType => + DynamicBadgeMode.values[_setting.get( + SettingBoxKey.dynamicBadgeMode, + defaultValue: DynamicBadgeMode.number.index, + )]; + + static DynamicBadgeMode get msgBadgeMode => + DynamicBadgeMode.values[_setting.get( + SettingBoxKey.msgBadgeMode, + defaultValue: DynamicBadgeMode.number.index, + )]; + + static Set get msgUnReadTypeV2 => + (_setting.get(SettingBoxKey.msgUnReadTypeV2) as List?) + ?.map((index) => MsgUnReadType.values[index]) + .toSet() ?? + MsgUnReadType.values.toSet(); + + static int get defaultHomePage => + _setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0); + + static int get previewQ => + _setting.get(SettingBoxKey.previewQuality, defaultValue: 100); + + static double get smallCardWidth => + _setting.get(SettingBoxKey.smallCardWidth, defaultValue: 240.0); + + static UpPanelPosition get upPanelPosition => + UpPanelPosition.values[_setting.get(SettingBoxKey.upPanelPosition, + defaultValue: UpPanelPosition.leftFixed.index)]; + + static int get fullScreenMode => _setting.get(SettingBoxKey.fullScreenMode, + defaultValue: FullScreenMode.auto.index); + + static int get btmProgressBehavior => + _setting.get(SettingBoxKey.btmProgressBehavior, + defaultValue: BtmProgressBehavior.alwaysShow.index); + + static String get subtitlePreference => + _setting.get(SettingBoxKey.subtitlePreference, + defaultValue: SubtitlePrefType.off.code); + + static bool get useRelativeSlide => + _setting.get(SettingBoxKey.useRelativeSlide, defaultValue: false); + + static int get sliderDuration => + _setting.get(SettingBoxKey.sliderDuration, defaultValue: 90); + + static int get defaultVideoQa => _setting.get( + SettingBoxKey.defaultVideoQa, + defaultValue: VideoQuality.values.last.code, + ); + + static int get defaultVideoQaCellular => _setting.get( + SettingBoxKey.defaultVideoQaCellular, + defaultValue: VideoQuality.high1080.code, + ); + + static int get defaultAudioQa => _setting.get( + SettingBoxKey.defaultAudioQa, + defaultValue: AudioQuality.values.last.code, + ); + + static int get defaultAudioQaCellular => _setting.get( + SettingBoxKey.defaultAudioQaCellular, + defaultValue: AudioQuality.k192.code, + ); + + static String get defaultDecode => _setting.get( + SettingBoxKey.defaultDecode, + defaultValue: VideoDecodeFormatType.values.last.code, + ); + + static String get secondDecode => _setting.get( + SettingBoxKey.secondDecode, + defaultValue: VideoDecodeFormatType.AV1.code, + ); + + static String get hardwareDecoding => _setting.get( + SettingBoxKey.hardwareDecoding, + defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto', + ); + + static String get videoSync => + _setting.get(SettingBoxKey.videoSync, defaultValue: 'display-resample'); + + static String get defaultCDNService => _setting.get( + SettingBoxKey.CDNService, + defaultValue: CDNService.backupUrl.code, + ); + + static String get banWordForRecommend => + _setting.get(SettingBoxKey.banWordForRecommend, defaultValue: ''); + + static String get banWordForReply => + _setting.get(SettingBoxKey.banWordForReply, defaultValue: ''); + + static String get banWordForZone => + _setting.get(SettingBoxKey.banWordForZone, defaultValue: ''); + + static bool get appRcmd => + _setting.get(SettingBoxKey.appRcmd, defaultValue: true); + + static String get systemProxyHost => + _setting.get(SettingBoxKey.systemProxyHost, defaultValue: ''); + + static String get systemProxyPort => + _setting.get(SettingBoxKey.systemProxyPort, defaultValue: ''); + + static int get defaultDynamicType => + _setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0); + + static double get blockLimit => + _setting.get(SettingBoxKey.blockLimit, defaultValue: 0.0); + + static double get refreshDragPercentage => + _setting.get(SettingBoxKey.refreshDragPercentage, defaultValue: 0.25); + + static double get refreshDisplacement => + _setting.get(SettingBoxKey.refreshDisplacement, defaultValue: 20.0); + + static String get blockUserID { + String blockUserID = + _setting.get(SettingBoxKey.blockUserID, defaultValue: ''); + if (blockUserID.isEmpty) { + blockUserID = const Uuid().v4().replaceAll('-', ''); + _setting.put(SettingBoxKey.blockUserID, blockUserID); + } + return blockUserID; + } + + static bool get blockToast => + _setting.get(SettingBoxKey.blockToast, defaultValue: true); + + static String get blockServer => _setting.get(SettingBoxKey.blockServer, + defaultValue: HttpString.sponsorBlockBaseUrl); + + static bool get blockTrack => + _setting.get(SettingBoxKey.blockTrack, defaultValue: true); + + static bool get checkDynamic => + _setting.get(SettingBoxKey.checkDynamic, defaultValue: true); + + static int get dynamicPeriod => + _setting.get(SettingBoxKey.dynamicPeriod, defaultValue: 5); + + static int get schemeVariant => + _setting.get(SettingBoxKey.schemeVariant, defaultValue: 10); + + static double get danmakuFontScaleFS => + _setting.get(SettingBoxKey.danmakuFontScaleFS, defaultValue: 1.2); + + static bool get danmakuMassiveMode => + _setting.get(SettingBoxKey.danmakuMassiveMode, defaultValue: false); + + static double get subtitleFontScale => + _setting.get(SettingBoxKey.subtitleFontScale, defaultValue: 1.0); + + static double get subtitleFontScaleFS => + _setting.get(SettingBoxKey.subtitleFontScaleFS, defaultValue: 1.5); + + static bool get showViewPoints => + _setting.get(SettingBoxKey.showViewPoints, defaultValue: true); + + static bool get showRelatedVideo => + _setting.get(SettingBoxKey.showRelatedVideo, defaultValue: true); + + static bool get showVideoReply => + _setting.get(SettingBoxKey.showVideoReply, defaultValue: true); + + static bool get showBangumiReply => + _setting.get(SettingBoxKey.showBangumiReply, defaultValue: true); + + static bool get alwaysExapndIntroPanel => + _setting.get(SettingBoxKey.alwaysExapndIntroPanel, defaultValue: false); + + static bool get exapndIntroPanelH => + _setting.get(SettingBoxKey.exapndIntroPanelH, defaultValue: false); + + static bool get horizontalSeasonPanel => + _setting.get(SettingBoxKey.horizontalSeasonPanel, defaultValue: false); + + static bool get horizontalMemberPage => + _setting.get(SettingBoxKey.horizontalMemberPage, defaultValue: false); + + static int get replyLengthLimit => + _setting.get(SettingBoxKey.replyLengthLimit, defaultValue: 6); + + static int get defaultPicQa => + _setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); + + static double get danmakuLineHeight => + _setting.get(SettingBoxKey.danmakuLineHeight, defaultValue: 1.6); + + static bool get showArgueMsg => + _setting.get(SettingBoxKey.showArgueMsg, defaultValue: true); + + static bool get reverseFromFirst => + _setting.get(SettingBoxKey.reverseFromFirst, defaultValue: true); + + static int get subtitlePaddingH => + _setting.get(SettingBoxKey.subtitlePaddingH, defaultValue: 24); + + static int get subtitlePaddingB => + _setting.get(SettingBoxKey.subtitlePaddingB, defaultValue: 24); + + static double get subtitleBgOpaticy => + _setting.get(SettingBoxKey.subtitleBgOpaticy, defaultValue: 0.67); + + static double get subtitleStrokeWidth => + _setting.get(SettingBoxKey.subtitleStrokeWidth, defaultValue: 2.0); + + static int get subtitleFontWeight => + _setting.get(SettingBoxKey.subtitleFontWeight, defaultValue: 5); + + static bool get badCertificateCallback => + _setting.get(SettingBoxKey.badCertificateCallback, defaultValue: false); + + static bool get continuePlayingPart => + _setting.get(SettingBoxKey.continuePlayingPart, defaultValue: true); + + static bool get cdnSpeedTest => + _setting.get(SettingBoxKey.cdnSpeedTest, defaultValue: true); + + static bool get autoUpdate => + _setting.get(SettingBoxKey.autoUpdate, defaultValue: true); + + static bool get horizontalPreview => + _setting.get(SettingBoxKey.horizontalPreview, defaultValue: false); + + static bool get openInBrowser => + _setting.get(SettingBoxKey.openInBrowser, defaultValue: false); + + static bool get savedRcmdTip => + _setting.get(SettingBoxKey.savedRcmdTip, defaultValue: true); + + static bool get showVipDanmaku => + _setting.get(SettingBoxKey.showVipDanmaku, defaultValue: true); + + static bool get showSpecialDanmaku => + _setting.get(SettingBoxKey.showSpecialDanmaku, defaultValue: false); + + static bool get mergeDanmaku => + _setting.get(SettingBoxKey.mergeDanmaku, defaultValue: false); + + static bool get showHotRcmd => + _setting.get(SettingBoxKey.showHotRcmd, defaultValue: false); + + static String get audioNormalization => + _setting.get(SettingBoxKey.audioNormalization, defaultValue: '0'); + + static int get superResolutionType => + _setting.get(SettingBoxKey.superResolutionType, defaultValue: 0); + + static bool get preInitPlayer => + _setting.get(SettingBoxKey.preInitPlayer, defaultValue: false); + + static bool get mainTabBarView => + _setting.get(SettingBoxKey.mainTabBarView, defaultValue: false); + + static bool get searchSuggestion => + _setting.get(SettingBoxKey.searchSuggestion, defaultValue: true); + + static bool get showDynDecorate => + _setting.get(SettingBoxKey.showDynDecorate, defaultValue: true); + + static bool get enableLivePhoto => + _setting.get(SettingBoxKey.enableLivePhoto, defaultValue: true); + + static bool get showSeekPreview => + _setting.get(SettingBoxKey.showSeekPreview, defaultValue: true); + + static bool get showDmChart => + _setting.get(SettingBoxKey.showDmChart, defaultValue: false); + + static bool get enableCommAntifraud => + _setting.get(SettingBoxKey.enableCommAntifraud, defaultValue: false); + + static bool get biliSendCommAntifraud => + _setting.get(SettingBoxKey.biliSendCommAntifraud, defaultValue: false); + + static bool get enableCreateDynAntifraud => + _setting.get(SettingBoxKey.enableCreateDynAntifraud, defaultValue: false); + + static bool get coinWithLike => + _setting.get(SettingBoxKey.coinWithLike, defaultValue: false); + + static bool get isPureBlackTheme => + _setting.get(SettingBoxKey.isPureBlackTheme, defaultValue: false); + + static bool get antiGoodsDyn => + _setting.get(SettingBoxKey.antiGoodsDyn, defaultValue: false); + + static bool get antiGoodsReply => + _setting.get(SettingBoxKey.antiGoodsReply, defaultValue: false); + + static bool get expandDynLivePanel => + _setting.get(SettingBoxKey.expandDynLivePanel, defaultValue: false); + + static bool get slideDismissReplyPage => _setting + .get(SettingBoxKey.slideDismissReplyPage, defaultValue: Platform.isIOS); + + static bool get showFSActionItem => + _setting.get(SettingBoxKey.showFSActionItem, defaultValue: true); + + static bool get enableShrinkVideoSize => + _setting.get(SettingBoxKey.enableShrinkVideoSize, defaultValue: true); + + static bool get showDynActionBar => + _setting.get(SettingBoxKey.showDynActionBar, defaultValue: true); + + static bool get darkVideoPage => + _setting.get(SettingBoxKey.darkVideoPage, defaultValue: false); + + static bool get enableSlideVolumeBrightness => _setting + .get(SettingBoxKey.enableSlideVolumeBrightness, defaultValue: true); + + static bool get enableSlideFS => + _setting.get(SettingBoxKey.enableSlideFS, defaultValue: true); + + static int get retryCount => + _setting.get(SettingBoxKey.retryCount, defaultValue: 2); + + static int get retryDelay => + _setting.get(SettingBoxKey.retryDelay, defaultValue: 500); + + static int get liveQuality => _setting.get(SettingBoxKey.liveQuality, + defaultValue: LiveQuality.origin.code); + + static int get liveQualityCellular => + _setting.get(SettingBoxKey.liveQualityCellular, + defaultValue: LiveQuality.superHD.code); + + static int get appFontWeight => + _setting.get(SettingBoxKey.appFontWeight, defaultValue: -1); + + static bool get enableDragSubtitle => + _setting.get(SettingBoxKey.enableDragSubtitle, defaultValue: false); + + static int get fastForBackwardDuration => + _setting.get(SettingBoxKey.fastForBackwardDuration, defaultValue: 10); + + static bool get recordSearchHistory => + _setting.get(SettingBoxKey.recordSearchHistory, defaultValue: true); + + static bool get navSearchStreamDebounce => + _setting.get(SettingBoxKey.navSearchStreamDebounce, defaultValue: false); + + static String get webdavUri => + _setting.get(SettingBoxKey.webdavUri, defaultValue: ''); + + static String get webdavUsername => + _setting.get(SettingBoxKey.webdavUsername, defaultValue: ''); + + static String get webdavPassword => + _setting.get(SettingBoxKey.webdavPassword, defaultValue: ''); + + static String get webdavDirectory => + _setting.get(SettingBoxKey.webdavDirectory, defaultValue: '/'); + + static bool get showPgcTimeline => + _setting.get(SettingBoxKey.showPgcTimeline, defaultValue: true); + + static num get maxCacheSize => + _setting.get(SettingBoxKey.maxCacheSize) ?? pow(1024, 3); + + static bool get optTabletNav => + _setting.get(SettingBoxKey.optTabletNav, defaultValue: true); + + static bool get horizontalScreen { + return _setting.get(SettingBoxKey.horizontalScreen) ?? isTablet; + } + + static bool get isTablet { + bool isTablet; + if (Get.context != null) { + isTablet = Get.context!.isTablet; + } else { + final view = WidgetsBinding.instance.platformDispatcher.views.first; + final screenSize = view.physicalSize / view.devicePixelRatio; + final shortestSide = min(screenSize.width.abs(), screenSize.height.abs()); + isTablet = shortestSide >= 600; + } + return isTablet; + } + + static String get banWordForDyn => + _setting.get(SettingBoxKey.banWordForDyn, defaultValue: ''); + + static bool get enableLog => + _setting.get(SettingBoxKey.enableLog, defaultValue: true); + + static bool get disableAudioCDN => + _setting.get(SettingBoxKey.disableAudioCDN, defaultValue: true); + + static int get minDurationForRcmd => + _setting.get(SettingBoxKey.minDurationForRcmd, defaultValue: 0); + + static int get minPlayForRcmd => + _setting.get(SettingBoxKey.minPlayForRcmd, defaultValue: 0); + + static int get minLikeRatioForRecommend => + _setting.get(SettingBoxKey.minLikeRatioForRecommend, defaultValue: 0); + + static bool get exemptFilterForFollowed => + _setting.get(SettingBoxKey.exemptFilterForFollowed, defaultValue: true); + + static bool get applyFilterToRelatedVideos => _setting + .get(SettingBoxKey.applyFilterToRelatedVideos, defaultValue: true); + + static bool get enableBackgroundPlay => + _setting.get(SettingBoxKey.enableBackgroundPlay, defaultValue: true); + + static bool get allowRotateScreen => + _setting.get(SettingBoxKey.allowRotateScreen, defaultValue: true); + + static bool get disableLikeMsg => + _setting.get(SettingBoxKey.disableLikeMsg, defaultValue: false); + + static bool get enableWordRe => + _setting.get(SettingBoxKey.enableWordRe, defaultValue: false); + + static bool get autoExitFullscreen => + _setting.get(SettingBoxKey.enableAutoExit, defaultValue: true); + + static bool get autoPlayEnable => + _setting.get(SettingBoxKey.autoPlayEnable, defaultValue: false); + + static bool get pipNoDanmaku => + _setting.get(SettingBoxKey.pipNoDanmaku, defaultValue: false); + + static bool get enableVerticalExpand => + _setting.get(SettingBoxKey.enableVerticalExpand, defaultValue: false); + + static bool get removeSafeArea => _setting + .get(SettingBoxKey.videoPlayerRemoveSafeArea, defaultValue: false); + + static double get defaultTextScale => + _setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0); + + static bool get dynamicsWaterfallFlow => + _setting.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); + + static bool get hideSearchBar => + _setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); + + static bool get enableSearchWord => + _setting.get(SettingBoxKey.enableSearchWord, defaultValue: true); + + static bool get useSideBar => + _setting.get(SettingBoxKey.useSideBar, defaultValue: false); + + static bool get dynamicsShowAllFollowedUp => _setting + .get(SettingBoxKey.dynamicsShowAllFollowedUp, defaultValue: false); + + static bool get enableShowDanmaku => + _setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: true); + + static bool get enableQuickFav => + _setting.get(SettingBoxKey.enableQuickFav, defaultValue: false); + + static bool get p1080 => + _setting.get(SettingBoxKey.p1080, defaultValue: true); + + static int get customColor => + _setting.get(SettingBoxKey.customColor, defaultValue: 0); + + static bool get dynamicColor => + _setting.get(SettingBoxKey.dynamicColor, defaultValue: true); + + static bool get autoClearCache => + _setting.get(SettingBoxKey.autoClearCache, defaultValue: false); + + static bool get enableSystemProxy => + _setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false); + + static bool get enableHttp2 => + _setting.get(SettingBoxKey.enableHttp2, defaultValue: false); + + static bool get enableRcmdDynamic => + _setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true); + + static int get replySortType => + _setting.get(SettingBoxKey.replySortType, defaultValue: 1); + + static bool get hideTabBar => + _setting.get(SettingBoxKey.hideTabBar, defaultValue: true); + + static int get dynamicBadgeMode => + _setting.get(SettingBoxKey.dynamicBadgeMode, + defaultValue: DynamicBadgeMode.number.index); + + static bool get enableMYBar => + _setting.get(SettingBoxKey.enableMYBar, defaultValue: true); + + static int get pageTransition => _setting.get(SettingBoxKey.pageTransition, + defaultValue: Transition.native.index); + + static bool get enableQuickDouble => + _setting.get(SettingBoxKey.enableQuickDouble, defaultValue: true); + + static bool get fullScreenGestureReverse => + _setting.get(SettingBoxKey.fullScreenGestureReverse, defaultValue: false); + + static bool get autoPiP => + _setting.get(SettingBoxKey.autoPiP, defaultValue: false); + + static bool get enableSponsorBlock => + _setting.get(SettingBoxKey.enableSponsorBlock, defaultValue: false); + + static bool get enableHA => + _setting.get(SettingBoxKey.enableHA, defaultValue: true); + + static Set get danmakuBlockType => + (_setting.get(SettingBoxKey.danmakuBlockType, defaultValue: []) + as Iterable) + .cast() + .toSet(); + + static int get danmakuWeight => + _setting.get(SettingBoxKey.danmakuWeight, defaultValue: 0); + + static double get danmakuShowArea => + _setting.get(SettingBoxKey.danmakuShowArea, defaultValue: 0.5); + + static double get danmakuOpacity => + _setting.get(SettingBoxKey.danmakuOpacity, defaultValue: 1.0); + + static double get danmakuFontScale => + _setting.get(SettingBoxKey.danmakuFontScale, defaultValue: 1.0); + + static double get danmakuDuration => + _setting.get(SettingBoxKey.danmakuDuration, defaultValue: 7.0); + + static double get danmakuStaticDuration => + _setting.get(SettingBoxKey.danmakuStaticDuration, defaultValue: 4.0); + + static double get strokeWidth => + _setting.get(SettingBoxKey.strokeWidth, defaultValue: 1.5); + + static int get fontWeight => + _setting.get(SettingBoxKey.fontWeight, defaultValue: 5); + + static bool get enableLongShowControl => + _setting.get(SettingBoxKey.enableLongShowControl, defaultValue: false); + + static bool get expandBuffer => + _setting.get(SettingBoxKey.expandBuffer, defaultValue: false); + + static bool get useOpenSLES => + _setting.get(SettingBoxKey.useOpenSLES, defaultValue: true); + + static bool get enableAi => + _setting.get(SettingBoxKey.enableAi, defaultValue: false); + + static bool get enableOnlineTotal => + _setting.get(SettingBoxKey.enableOnlineTotal, defaultValue: false); + + static bool get enableAutoEnter => + _setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false); + + static bool get enableAutoLongPressSpeed => + _setting.get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); + + static double get playSpeedDefault => + _video.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); + + static double get longPressSpeedDefault => + _video.get(VideoBoxKey.longPressSpeedDefault, defaultValue: 3.0); + + static bool get defaultShowComment => + _setting.get(SettingBoxKey.defaultShowComment, defaultValue: false); + + static bool get enableHotKey => + _setting.get(SettingBoxKey.enableHotKey, defaultValue: true); + + static bool get enableSearchRcmd => + _setting.get(SettingBoxKey.enableSearchRcmd, defaultValue: true); + + static bool get enableSaveLastData => + _setting.get(SettingBoxKey.enableSaveLastData, defaultValue: false); + + static double get defaultToastOp => + _setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0); + + static int get playRepeat => + (_video.get(VideoBoxKey.playRepeat) as num?)?.toInt() ?? + PlayRepeat.pause.index; + + static int get cacheVideoFit => + _video.get(VideoBoxKey.cacheVideoFit, defaultValue: 1); + + static bool get continuePlayInBackground => + _setting.get(SettingBoxKey.continuePlayInBackground, defaultValue: false); +} diff --git a/lib/utils/theme_utils.dart b/lib/utils/theme_utils.dart index a99dff4fe..e961f72bd 100644 --- a/lib/utils/theme_utils.dart +++ b/lib/utils/theme_utils.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/main.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flex_seed_scheme/flex_seed_scheme.dart'; import 'package:flutter/material.dart'; @@ -12,7 +12,7 @@ class ThemeUtils { required FlexSchemeVariant variant, }) { final appFontWeight = - GStorage.appFontWeight.clamp(-1, FontWeight.values.length - 1); + Pref.appFontWeight.clamp(-1, FontWeight.values.length - 1); final fontWeight = appFontWeight == -1 ? null : FontWeight.values[appFontWeight]; late final textStyle = TextStyle(fontWeight: fontWeight); @@ -111,10 +111,10 @@ class ThemeUtils { ), ); if (isDark) { - if (GStorage.isPureBlackTheme) { + if (Pref.isPureBlackTheme) { themeData = darkenTheme(themeData); } - if (GStorage.darkVideoPage) { + if (Pref.darkVideoPage) { MyApp.darkThemeData = themeData; } } diff --git a/lib/utils/update.dart b/lib/utils/update.dart index c9f12dba6..73ff0f394 100644 --- a/lib/utils/update.dart +++ b/lib/utils/update.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; diff --git a/lib/utils/video_utils.dart b/lib/utils/video_utils.dart index 4179b0744..dbbee2998 100644 --- a/lib/utils/video_utils.dart +++ b/lib/utils/video_utils.dart @@ -2,16 +2,18 @@ import 'package:PiliPlus/models/common/video/cdn_type.dart'; import 'package:PiliPlus/models/video/play/url.dart'; import 'package:PiliPlus/models_new/live/live_room_play_info/codec.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; class VideoUtils { + static String cdnService = Pref.defaultCDNService; + static bool disableAudioCDN = Pref.disableAudioCDN; + static String getCdnUrl(dynamic item, [String? defaultCDNService]) { String? backupUrl; String? videoUrl; - defaultCDNService ??= GStorage.defaultCDNService; + defaultCDNService ??= cdnService; if (item is AudioItem) { - if (GStorage.setting - .get(SettingBoxKey.disableAudioCDN, defaultValue: true)) { + if (disableAudioCDN) { return item.backupUrl?.isNotEmpty == true ? item.backupUrl! : item.baseUrl ?? ""; diff --git a/lib/utils/wbi_sign.dart b/lib/utils/wbi_sign.dart index 19a4b2ba8..846663a7d 100644 --- a/lib/utils/wbi_sign.dart +++ b/lib/utils/wbi_sign.dart @@ -7,13 +7,14 @@ import 'dart:convert'; import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:crypto/crypto.dart'; import 'package:hive/hive.dart'; import 'package:synchronized/synchronized.dart'; class WbiSign { - static Box get localCache => GStorage.localCache; + static Box localCache = GStorage.localCache; static final Lock lock = Lock(); static final RegExp chrFilter = RegExp(r"[!\'\(\)\*]"); static const mixinKeyEncTab = [