diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index fb6bfc41..aa9d360c 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -1,4 +1,5 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:miru_app/data/providers/anilist_provider.dart'; @@ -8,6 +9,10 @@ import 'package:miru_app/data/services/database_service.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:extended_image/extended_image.dart'; import 'package:miru_app/utils/miru_storage.dart'; +import 'dart:async'; +import 'package:battery_plus/battery_plus.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; +import 'package:miru_app/utils/i18n.dart'; class ComicController extends ReaderController { ComicController({ @@ -38,17 +43,39 @@ class ComicController extends ReaderController { final itemPositionsListener = ItemPositionsListener.create(); final itemScrollController = ItemScrollController(); final scrollOffsetController = ScrollOffsetController(); - + final alignMode = Alignment.bottomLeft.obs; // 是否已经恢复上次阅读 final isRecover = false.obs; - + final batteryLevel = 100.obs; // 是否按下 ctrl - + Timer? _barreryTimer; + final statusBarElement = { + 'reader-setting.battery'.i18n: true.obs, + 'reader-setting.time'.i18n: true.obs, + 'reader-setting.page-indicator'.i18n: true.obs, + 'reader-setting.battery-icon'.i18n: true.obs, + }; final isZoom = false.obs; + final currentTime = "".obs; + Future _statusBar([Timer? t]) async { + final battery = Battery(); + batteryLevel.value = await battery.batteryLevel; + final datenow = DateTime.now(); + final hour = datenow.hour < 10 ? "0${datenow.hour}" : datenow.hour; + final minute = datenow.minute < 10 ? "0${datenow.minute}" : datenow.minute; + currentTime.value = "$hour:$minute"; + debugPrint("${datenow.toLocal()}"); + } @override - void onInit() { + void onInit() async { _initSetting(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + WakelockPlus.toggle( + enable: MiruStorage.getSetting(SettingKey.enableWakelock)); + await _statusBar(); + _barreryTimer = Timer.periodic( + const Duration(seconds: 10), (timer) => _statusBar(timer)); itemPositionsListener.itemPositions.addListener(() { if (itemPositionsListener.itemPositions.value.isEmpty) { return; @@ -196,6 +223,9 @@ class ComicController extends ReaderController { mediaId: anilistID, ); } + _barreryTimer!.cancel(); + WakelockPlus.disable(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.onClose(); } } diff --git a/lib/controllers/watch/novel_controller.dart b/lib/controllers/watch/novel_controller.dart index 27c760ef..cb0e4b69 100644 --- a/lib/controllers/watch/novel_controller.dart +++ b/lib/controllers/watch/novel_controller.dart @@ -4,6 +4,8 @@ import 'package:miru_app/controllers/watch/reader_controller.dart'; import 'package:miru_app/data/services/database_service.dart'; import 'package:miru_app/utils/miru_storage.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; +import 'package:flutter/services.dart'; class NovelController extends ReaderController { NovelController({ @@ -26,8 +28,10 @@ class NovelController extends ReaderController { @override void onInit() { super.onInit(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); fontSize.value = MiruStorage.getSetting(SettingKey.novelFontSize); - + WakelockPlus.toggle( + enable: MiruStorage.getSetting(SettingKey.enableWakelock)); itemPositionsListener.itemPositions.addListener(() { if (itemPositionsListener.itemPositions.value.isEmpty) { return; @@ -72,6 +76,7 @@ class NovelController extends ReaderController { totalProgress, ); } + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.onClose(); } } diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index 3a8540d9..e4f80242 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -129,6 +129,7 @@ class MiruStorage { await _initSetting(SettingKey.proxy, ''); await _initSetting(SettingKey.proxyType, 'DIRECT'); await _initSetting(SettingKey.saveLog, true); + await _initSetting(SettingKey.enableWakelock, false); } static _initSetting(String key, dynamic value) async { @@ -176,7 +177,9 @@ class SettingKey { static String keyJ = 'KeyJ'; static String arrowLeft = 'Arrowleft'; static String arrowRight = 'Arrowright'; + //reading mode static String readingMode = 'ReadingMode'; + static String enableWakelock = 'EnableWakelock'; static String aniListToken = 'AniListToken'; static String aniListUserId = 'AniListUserId'; static String autoTracking = 'AutoTracking'; diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index e45523b5..56f5d3b3 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:fluent_ui/fluent_ui.dart' as fluent; @@ -12,6 +11,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:miru_app/views/widgets/progress.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:extended_image/extended_image.dart'; +import 'package:based_battery_indicator/based_battery_indicator.dart'; class ComicReaderContent extends StatefulWidget { const ComicReaderContent(this.tag, {super.key}); @@ -22,11 +22,6 @@ class ComicReaderContent extends StatefulWidget { } class _ComicReaderContentState extends State { - @override - void initState() { - super.initState(); - } - late final _c = Get.find(tag: widget.tag); // 按下数量 @@ -46,27 +41,66 @@ class _ComicReaderContentState extends State { ); } - _buildDisplay(Widget child) { + Widget _buildDisplay(Widget child) { + if (_c.statusBarElement.values.every((element) => element.value == false)) { + return child; + } return Stack( children: [ child, - Positioned( - bottom: 0, - child: Container( - color: Colors.black.withAlpha(200), - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 2), - child: Obx( - () => Text( - "${_c.currentPage.value + 1}/${_c.watchData.value?.urls.length ?? 0}", - style: const TextStyle(color: Colors.white, fontSize: 15), + Obx(() => Align( + alignment: _c.alignMode.value, + child: Container( + color: Colors.black.withAlpha(200), + padding: const EdgeInsets.fromLTRB(20, 2, 12, 2), + child: _indicatorBuilder(), ), - ), - ), - ), + )), ], ); } + Widget _indicatorBuilder() { + return Obx(() => Row(mainAxisSize: MainAxisSize.min, children: [ + if (_c.statusBarElement["reader-setting.page-indicator".i18n]! + .value) ...[ + Text( + "${_c.currentPage.value + 1}/${_c.watchData.value?.urls.length ?? 0}", + style: const TextStyle(color: Colors.white, fontSize: 15), + ), + const SizedBox(width: 8) + ], + if (_c + .statusBarElement["reader-setting.battery-icon".i18n]!.value) ...[ + BasedBatteryIndicator( + status: BasedBatteryStatus( + value: _c.batteryLevel.value, + type: BasedBatteryStatusType.normal, + ), + trackHeight: 10.0, + trackAspectRatio: 2.0, + curve: Curves.ease, + duration: const Duration(seconds: 10), + ), + const SizedBox(width: 8) + ], + if (_c.statusBarElement["reader-setting.battery".i18n]!.value) ...[ + Text( + "${_c.batteryLevel.value}%", + style: const TextStyle(color: Colors.white, fontSize: 15), + ), + const SizedBox(width: 8) + ], + if (_c.statusBarElement["reader-setting.time".i18n]!.value) ...[ + Text( + _c.currentTime.value, + style: const TextStyle(color: Colors.white, fontSize: 15), + ), + const SizedBox(width: 8) + ], + ])); + } + _buildContent() { late Color backgroundColor; if (Platform.isAndroid) { diff --git a/lib/views/pages/watch/reader/comic/comic_reader_settings.dart b/lib/views/pages/watch/reader/comic/comic_reader_settings.dart index 7e494f49..ba7f7e03 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_settings.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_settings.dart @@ -4,7 +4,10 @@ import 'package:get/get.dart'; import 'package:miru_app/models/index.dart'; import 'package:miru_app/controllers/watch/comic_controller.dart'; import 'package:miru_app/utils/i18n.dart'; +import 'package:miru_app/utils/miru_storage.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; +import 'package:miru_app/views/widgets/settings/settings_switch_tile.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; class ComicReaderSettings extends StatefulWidget { const ComicReaderSettings(this.tag, {super.key}); @@ -20,43 +23,100 @@ class _ComicReaderSettingsState extends State { Widget _buildAndroid(BuildContext context) { return Padding( padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 阅读模式 - Text('comic-settings.read-mode'.i18n), - const SizedBox(height: 16), - SizedBox( - width: double.infinity, - child: SegmentedButton( - segments: [ - ButtonSegment( - value: MangaReadMode.standard, - label: Text('comic-settings.standard'.i18n), - ), - ButtonSegment( - value: MangaReadMode.rightToLeft, - label: Text('comic-settings.right-to-left'.i18n), + child: Obx(() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 阅读模式 + + Text('comic-settings.read-mode'.i18n), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + child: SegmentedButton( + segments: [ + ButtonSegment( + value: MangaReadMode.standard, + label: Text('comic-settings.standard'.i18n), + ), + ButtonSegment( + value: MangaReadMode.rightToLeft, + label: Text('comic-settings.right-to-left'.i18n), + ), + ButtonSegment( + value: MangaReadMode.webTonn, + label: Text('comic-settings.web-tonn'.i18n), + ), + ], + selected: {_c.readType.value}, + onSelectionChanged: (value) { + if (value.isNotEmpty) { + _c.readType.value = value.first; + } + }, + showSelectedIcon: false, ), - ButtonSegment( - value: MangaReadMode.webTonn, - label: Text('comic-settings.web-tonn'.i18n), + ), + + const SizedBox(height: 16), + Text('comic-settings.indicator-alignment'.i18n), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + child: SegmentedButton( + segments: [ + ButtonSegment( + value: Alignment.bottomLeft, + label: Text('comic-settings.bottomLeft'.i18n), + ), + ButtonSegment( + value: Alignment.bottomRight, + label: Text('comic-settings.rightLeft'.i18n), + ), + ButtonSegment( + value: Alignment.topLeft, + label: Text('comic-settings.topLeft'.i18n), + ), + ButtonSegment( + value: Alignment.topRight, + label: Text('comic-settings.topRight'.i18n), + ) + ], + selected: {_c.alignMode.value}, + onSelectionChanged: (value) { + if (value.isNotEmpty) { + _c.alignMode.value = value.first; + } + }, + showSelectedIcon: false, ), - ], - selected: {_c.readType.value}, - onSelectionChanged: (value) { - if (value.isNotEmpty) { - setState(() { - _c.readType.value = value.first; - }); - } - }, - showSelectedIcon: false, - ), - ), - ], - ), + ), + const SizedBox(height: 16), + Text('comic-settings.status-bar'.i18n), + const SizedBox(height: 5), + Wrap( + spacing: 5, + children: _c.statusBarElement.keys + .map((e) => FilterChip( + label: Text(e), + selected: _c.statusBarElement[e]!.value, + onSelected: (val) { + _c.statusBarElement[e]!.value = val; + })) + .toList(), + ), + SettingsSwitchTile( + icon: const Icon(Icons.coffee), + title: "reader-settings.enable-wakelock".i18n, + buildValue: () => + MiruStorage.getSetting(SettingKey.enableWakelock), + onChanged: (val) { + WakelockPlus.toggle(enable: val); + MiruStorage.setSetting(SettingKey.enableWakelock, val); + }), + const SizedBox(height: 16), + ], + )), ); } diff --git a/lib/views/pages/watch/reader/novel/novel_reader_settings.dart b/lib/views/pages/watch/reader/novel/novel_reader_settings.dart index 7dd0c4c9..162892eb 100644 --- a/lib/views/pages/watch/reader/novel/novel_reader_settings.dart +++ b/lib/views/pages/watch/reader/novel/novel_reader_settings.dart @@ -4,6 +4,9 @@ import 'package:get/get.dart'; import 'package:miru_app/controllers/watch/novel_controller.dart'; import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; +import 'package:miru_app/utils/miru_storage.dart'; +import 'package:miru_app/views/widgets/settings/settings_switch_tile.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; class NovelReaderSettings extends StatefulWidget { const NovelReaderSettings(this.tag, {super.key}); @@ -19,24 +22,32 @@ class _NovelReaderSettingsState extends State { Widget _buildAndroid(BuildContext context) { return Padding( padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text("novel-settings.font-size".i18n), - const SizedBox(height: 16), - Obx( - () => Slider( - value: _c.fontSize.value, - onChanged: (value) { - _c.fontSize.value = value; - }, - min: 12, - max: 24, - ), - ), - ], - ), + child: Obx(() => Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text("novel-settings.font-size".i18n), + const SizedBox(height: 16), + Slider( + value: _c.fontSize.value, + onChanged: (value) { + _c.fontSize.value = value; + }, + min: 12, + max: 24, + ), + const SizedBox(height: 16), + SettingsSwitchTile( + icon: const Icon(Icons.coffee), + title: "reader-settings.enable-wakelock".i18n, + buildValue: () => + MiruStorage.getSetting(SettingKey.enableWakelock), + onChanged: (val) { + WakelockPlus.toggle(enable: val); + MiruStorage.setSetting(SettingKey.enableWakelock, val); + }) + ], + )), ); } diff --git a/lib/views/widgets/watch/control_panel_header.dart b/lib/views/widgets/watch/control_panel_header.dart index 9c3a54fd..c27230c9 100644 --- a/lib/views/widgets/watch/control_panel_header.dart +++ b/lib/views/widgets/watch/control_panel_header.dart @@ -42,7 +42,13 @@ class _ControlPanelHeaderState onPressed: () { showModalBottomSheet( context: context, - builder: (context) => widget.buildSettings(context), + isScrollControlled: true, + builder: (context) => DraggableScrollableSheet( + expand: false, + builder: (context, controller) => SingleChildScrollView( + controller: controller, + child: widget.buildSettings(context)), + ), ); }, icon: const Icon(Icons.settings), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 882d01b7..5d9d8f79 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import battery_plus import desktop_multi_window import device_info_plus import flutter_inappwebview_macos @@ -22,6 +23,7 @@ import wakelock_plus import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin")) FlutterMultiWindowPlugin.register(with: registry.registrar(forPlugin: "FlutterMultiWindowPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) diff --git a/pubspec.lock b/pubspec.lock index ad2e560a..cfe0aa71 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + based_battery_indicator: + dependency: "direct main" + description: + name: based_battery_indicator + sha256: "61c5b5a33e5dc35fc45cb016160f4eae8ee9a85c804bd797a5dbb9283bc9f288" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + battery_plus: + dependency: "direct main" + description: + name: battery_plus + sha256: "0568fbba70697b8d0c34c1176faa2bc6d61c7fb211a2d2d64e493b91ff72d3f8" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + battery_plus_platform_interface: + dependency: transitive + description: + name: battery_plus_platform_interface + sha256: "942707f90e2f7481dcb178df02e22a9c6971b3562b848d6a1b8c7cff9f1a1fec" + url: "https://pub.dev" + source: hosted + version: "2.0.0" boolean_selector: dependency: transitive description: @@ -1347,6 +1371,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0+1" + upower: + dependency: transitive + description: + name: upower + sha256: cf042403154751180affa1d15614db7fa50234bc2373cd21c3db666c38543ebf + url: "https://pub.dev" + source: hosted + version: "0.7.0" uri_parser: dependency: transitive description: @@ -1444,7 +1476,7 @@ packages: source: hosted version: "2.0.7" wakelock_plus: - dependency: transitive + dependency: "direct main" description: name: wakelock_plus sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d diff --git a/pubspec.yaml b/pubspec.yaml index 914fe46b..7e3418b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,9 @@ dependencies: flutter_socks_proxy: ^0.0.3 logging: ^1.2.0 share_plus: ^7.2.1 + wakelock_plus: ^1.1.4 + battery_plus: ^5.0.2 + based_battery_indicator: ^1.0.3 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 2a40c6ec..c9e9bd24 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -19,6 +20,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + BatteryPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin")); DesktopMultiWindowPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopMultiWindowPlugin")); FlutterJsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 12c4f110..f3002f91 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + battery_plus desktop_multi_window flutter_js flutter_windows_webview