import 'package:background_fetch/background_fetch.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../background_tasks/scheduledTask.dart'; import '../../storage/base/settingsProvider.dart'; import '../../widget/centeredLeading.dart'; import '../../widget/confirmDialog.dart'; import '../../widget/debug/cacheView.dart'; import '../../widget/debug/jsonViewer.dart'; import '../../widget/infoDialog.dart'; class DevToolsSettings extends StatefulWidget { final SettingsProvider settings; const DevToolsSettings({required this.settings, super.key}); @override State createState() => _DevToolsSettingsState(); } class _DevToolsSettingsState extends State { @override Widget build(BuildContext context) => Column( children: [ ListTile( leading: const CenteredLeading(Icon(Icons.task_outlined)), title: const Text('Background app fetch task'), trailing: const Icon(Icons.arrow_right), onTap: () { showDialog(context: context, builder: (context) => AlertDialog( title: Text('Background fetch task'), content: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ FutureBuilder(future: BackgroundFetch.status, builder: (context, snapshot) { if(snapshot.hasData) { var fetchStatus = switch(snapshot.data) { BackgroundFetch.STATUS_AVAILABLE => 'STATUS_AVAILABLE, Background updates are available for the app.', BackgroundFetch.STATUS_DENIED => 'STATUS_DENIED, The user explicitly disabled background behavior for this app or for the whole system.', BackgroundFetch.STATUS_RESTRICTED => 'STATUS_RESTRICTED, Background updates are unavailable and the user cannot enable them again. For example, this status can occur when parental controls are in effect for the current user.', _ => 'UNKNOWN', }; return Text('(${snapshot.data}): $fetchStatus'); } return LinearProgressIndicator(); }), const Divider(), const Text('There is no indicator if the Fetch-API is currently running or not!'), const Divider(), FutureBuilder( future: SharedPreferences.getInstance(), builder: (context, snapshot) { if(!snapshot.hasData) return LinearProgressIndicator(); return Text('Last fetch timestamp: ${snapshot.data?.getStringList(ScheduledTask.fetchApiLastRunTimestampKey)?.last ?? 'No entry'}'); }, ) ], ), actions: [ FutureBuilder(future: SharedPreferences.getInstance(), builder: (context, snapshot) { if(!snapshot.hasData) return LinearProgressIndicator(); return TextButton( onPressed: () { InfoDialog.show( context, (snapshot.data!.getStringList(ScheduledTask.fetchApiLastRunTimestampKey) ?? []).reversed.join('\n') ); }, child: Text('Fetch history') ); }), TextButton( onPressed: () => ConfirmDialog( title: 'Warning', content: 'Background Fetch worker will be started! This basically happens on every app startup.', onConfirm: BackgroundFetch.start ).asDialog(context), child: Text('Fetch-API Start') ), TextButton( onPressed: () => ConfirmDialog( title: 'Warning', content: 'Background Fetch worker will be terminated. This will result in outdated Information when App is not in foreground!', onConfirm: BackgroundFetch.stop ).asDialog(context), child: Text('Fetch-API Stop') ), TextButton( onPressed: () => ConfirmDialog( title: 'Warning', content: 'Background fetch will run now! This happens in the application layer and does not interact with the Fetch-API!', confirmButton: 'Run', onConfirm: ScheduledTask.backgroundFetch ).asDialog(context), child: Text('Run task manually') ), TextButton(onPressed: () => Navigator.of(context).pop(), child: Text('Zurück')) ], )); }, ), ListTile( leading: const CenteredLeading(Icon(Icons.speed_outlined)), title: const Text('Performance overlays'), trailing: const Icon(Icons.arrow_right), onTap: () { showDialog(context: context, builder: (context) => SimpleDialog( children: [ ListTile( leading: const Icon(Icons.auto_graph_outlined), title: const Text('Performance graph'), trailing: Checkbox( value: widget.settings.val().devToolsSettings.showPerformanceOverlay, onChanged: (e) => widget.settings.val(write: true).devToolsSettings.showPerformanceOverlay = e!, ), ), ListTile( leading: const Icon(Icons.screen_search_desktop_outlined), title: const Text('Indicate offscreen layers'), trailing: Checkbox( value: widget.settings.val().devToolsSettings.checkerboardOffscreenLayers, onChanged: (e) => widget.settings.val(write: true).devToolsSettings.checkerboardOffscreenLayers = e!, ), ), ListTile( leading: const Icon(Icons.imagesearch_roller_outlined), title: const Text('Indicate raster cache images'), trailing: Checkbox( value: widget.settings.val().devToolsSettings.checkerboardRasterCacheImages, onChanged: (e) => widget.settings.val(write: true).devToolsSettings.checkerboardRasterCacheImages = e!, ), ), ], )); }, ), ListTile( leading: const CenteredLeading(Icon(Icons.image_outlined)), title: const Text('Thumb-storage'), subtitle: Text('etwa ${filesize(PaintingBinding.instance.imageCache.currentSizeBytes)}\nLange tippen um zu löschen'), onLongPress: () { ConfirmDialog( title: 'Thumbs cache löschen', content: 'Alle zwischengespeicherten Bilder werden gelöscht.', confirmButton: 'Unwiederruflich löschen', onConfirm: () => PaintingBinding.instance.imageCache.clear(), ).asDialog(context); }, ), ListTile( leading: const CenteredLeading(Icon(Icons.settings_applications_outlined)), title: const Text('Settings-storage JSON dump'), subtitle: Text('etwa ${filesize(widget.settings.val().toJson().toString().length * 8)}\nLange tippen um zu löschen'), onTap: () { JsonViewer.asDialog(context, widget.settings.val().toJson()); }, onLongPress: () { ConfirmDialog( title: 'Einstellungen löschen', content: 'Alle Einstellungen gehen verloren! Accountdaten sowie App-Daten sind nicht betroffen.', confirmButton: 'Unwiederruflich Löschen', onConfirm: () { Provider.of(context, listen: false).reset(); }, ).asDialog(context); }, trailing: const Icon(Icons.arrow_right), ), ListTile( leading: const CenteredLeading(Icon(Icons.data_object)), title: const Text('Cache-storage JSON dump'), subtitle: FutureBuilder( future: const CacheView().totalSize(), builder: (context, snapshot) => Text("etwa ${snapshot.hasError ? "?" : snapshot.hasData ? filesize(snapshot.data) : "..."}\nLange tippen um zu löschen"), ), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const CacheView())); }, onLongPress: () { ConfirmDialog( title: 'App-Cache löschen', content: 'Alle cache Einträge werden gelöscht. Der Cache wird bei Nutzung der App automatisch erneut aufgebaut', confirmButton: 'Unwiederruflich löschen', onConfirm: () => const CacheView().clear().then((value) => setState((){})), ).asDialog(context); }, trailing: const Icon(Icons.arrow_right), ), ListTile( leading: const CenteredLeading(Icon(Icons.data_object)), title: const Text('BLOC-storage state cache'), subtitle: const Text('Lange tippen um zu löschen'), onTap: () { // Navigator.push(context, MaterialPageRoute(builder: (context) => const CacheView())); }, onLongPress: () { ConfirmDialog( title: 'BLOC-Cache löschen', content: 'Alle cache Einträge werden gelöscht. Der Cache wird bei Nutzung der App automatisch erneut aufgebaut', confirmButton: 'Unwiederruflich löschen', onConfirm: () => HydratedBloc.storage.clear(), ).asDialog(context); }, ), ], ); }