diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 620d5a3d..20a2d876 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,38 +1,40 @@ - - + + + + + android:label="天使动漫" + android:name="${applicationName}" + android:usesCleartextTraffic="true" + android:icon="@mipmap/ic_launcher"> + android:name=".MainActivity" + android:exported="true" + android:launchMode="singleTop" + android:theme="@style/Theme.TsdmClient.Launcher" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" + android:hardwareAccelerated="true" + android:windowSoftInputMode="adjustResize"> - - + + + android:name="flutterEmbedding" + android:value="2" /> diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 33d6d24a..09f1d8ed 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -45,5 +45,10 @@ UIApplicationSupportsIndirectInputEvents + UIBackgroundModes + + fetch + processing + diff --git a/lib/features/settings/provider/settings_provider.dart b/lib/features/settings/provider/settings_provider.dart new file mode 100644 index 00000000..48fa729b --- /dev/null +++ b/lib/features/settings/provider/settings_provider.dart @@ -0,0 +1,30 @@ +// lib/features/settings/provider/settings_provider.dart +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../../services/background_service.dart'; + +class SettingsProvider with ChangeNotifier { + bool _backgroundKeepAlive = false; + + bool get backgroundKeepAlive => _backgroundKeepAlive; + + Future setBackgroundKeepAlive(bool value) async { + _backgroundKeepAlive = value; + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('background_keep_alive', value); + notifyListeners(); + + // 根据开关状态启动或停止后台任务 + if (value) { + await BackgroundService.startBackgroundTask(); + } else { + await BackgroundService.stopBackgroundTask(); + } + } + + Future loadBackgroundKeepAlive() async { + final prefs = await SharedPreferences.getInstance(); + _backgroundKeepAlive = prefs.getBool('background_keep_alive') ?? false; + notifyListeners(); + } +} diff --git a/lib/features/settings/view/settings_page.dart b/lib/features/settings/view/settings_page.dart index 1b986283..96a277ba 100644 --- a/lib/features/settings/view/settings_page.dart +++ b/lib/features/settings/view/settings_page.dart @@ -46,6 +46,8 @@ import 'package:tsdm_client/widgets/section_switch_list_tile.dart'; import 'package:tsdm_client/widgets/section_title_text.dart'; import 'package:tsdm_client/widgets/shutdown.dart'; import 'package:tsdm_client/widgets/tips.dart'; +import 'package:provider/provider.dart'; +import '../provider/settings_provider.dart'; /// Settings page of the app. class SettingsPage extends StatefulWidget { @@ -865,3 +867,48 @@ class _SettingsPageState extends State { ); } } + +Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text('设置'), + ), + child: SafeArea( + child: ListView( + children: [ + // ... 其他现有设置项 ... + + // === 添加后台常驻开关 === + CupertinoFormSection.insetGrouped( + header: Text('行为'), + children: [ + CupertinoFormRow( + prefix: Container( + width: 24, // 留空图标位置 + ), + helper: Text('开启后,应用在后台时会尝试保持活动状态以执行任务'), + child: Row( + children: [ + Expanded( + child: Text('后台常驻'), + ), + Consumer( + builder: (context, settings, child) { + return CupertinoSwitch( + value: settings.backgroundKeepAlive, + onChanged: (bool value) { + settings.setBackgroundKeepAlive(value); + }, + ); + }, + ), + ], + ), + ), + ], + ), + ], + ), + ), + ); +} diff --git a/lib/main.dart b/lib/main.dart index aebda4a0..148896f1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,6 +13,7 @@ import 'package:tsdm_client/features/local_notice/callback.dart'; import 'package:tsdm_client/features/settings/repositories/settings_repository.dart'; import 'package:tsdm_client/i18n/strings.g.dart'; import 'package:tsdm_client/instance.dart'; +import 'package:tsdm_client/services/background_service.dart'; import 'package:tsdm_client/shared/providers/providers.dart'; import 'package:tsdm_client/shared/providers/proxy_provider/proxy_provider.dart'; import 'package:tsdm_client/utils/platform.dart'; @@ -31,6 +32,18 @@ Future _boot(List args) async { talker.debug('------------------- start app -------------------'); await initProviders(); + // 初始化后台服务 + await BackgroundService.initialize(); + + // 获取设置仓库并检查后台常驻设置 + final settingsRepo = getIt.get(); + final backgroundKeepAliveEnabled = settingsRepo.currentSettings.backgroundKeepAlive ?? false; + + // 如果设置中启用了后台常驻,则启动后台任务 + if (backgroundKeepAliveEnabled) { + await BackgroundService.startBackgroundTask(); + } + final settings = getIt.get().currentSettings; final settingsLocale = settings.locale; diff --git a/lib/services/background_service.dart b/lib/services/background_service.dart new file mode 100644 index 00000000..84edcd06 --- /dev/null +++ b/lib/services/background_service.dart @@ -0,0 +1,53 @@ +import 'package:workmanager/workmanager.dart'; + +@pragma('vm:entry-point') +void callbackDispatcher() { + Workmanager().executeTask((task, inputData) { + print("BackgroundTask: TSDM Client is running in background!"); + return Future.value(true); + }); +} + +class BackgroundService { + static Future initialize() async { + try { + await Workmanager().initialize( + callbackDispatcher, + isInDebugMode: false, + ); + print("BackgroundService: Initialized successfully"); + } catch (e) { + print("BackgroundService: Initialization failed - $e"); + rethrow; + } + } + + static Future startBackgroundTask() async { + try { + await Workmanager().registerPeriodicTask( + "tsdmBackgroundTask", + "tsdmBackgroundTask", + frequency: Duration(minutes: 15), + initialDelay: Duration(seconds: 10), + constraints: Constraints( + networkType: NetworkType.connected, + ), + existingWorkPolicy: ExistingWorkPolicy.replace, + ); + print("BackgroundService: Background task started successfully"); + } catch (e) { + print("BackgroundService: Failed to start background task - $e"); + rethrow; + } + } + + static Future stopBackgroundTask() async { + try { + await Workmanager().cancelByUniqueName("tsdmBackgroundTask"); + print("BackgroundService: Background task stopped successfully"); + } catch (e) { + print("BackgroundService: Failed to stop background task - $e"); + rethrow; + } + } +}