import 'dart:io'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:stadtbote/main.dart'; import 'package:stadtbote/model/job.dart'; import 'package:stadtbote/model/log_entry.dart'; import 'package:stadtbote/tools/constants.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:uuid/uuid.dart'; import '../Tools/colored_safe_area.dart'; class Util { static Future preferencesContainsKey(String key) async { var prefs = await SharedPreferences.getInstance(); return prefs.containsKey(key); } static void preferencesSaveString(String key, String value) async { var prefs = await SharedPreferences.getInstance(); await prefs.setString(key, value); } static Future preferencesGetString(String key, String? defaultValue) async { if (await preferencesContainsKey(key)) { var prefs = await SharedPreferences.getInstance(); return prefs.getString(key); } return defaultValue; } static void preferencesSaveInt(String key, int value) async { var prefs = await SharedPreferences.getInstance(); await prefs.setInt(key, value); } static Future preferencesGetInt(String key, int? defaultValue) async { if (await preferencesContainsKey(key)) { var prefs = await SharedPreferences.getInstance(); return prefs.getInt(key); } return defaultValue; } static void preferencesSaveBool(String key, bool value) async { var prefs = await SharedPreferences.getInstance(); await prefs.setBool(key, value); } static Future preferencesGetBool(String key, bool defaultValue) async { if (await preferencesContainsKey(key)) { var prefs = await SharedPreferences.getInstance(); return prefs.getBool(key) as bool; } return defaultValue; } static Future getAppUuid() async { var currentUuid = await preferencesGetString('uuid', null); if (currentUuid == null) { currentUuid ??= const Uuid().v4(); preferencesSaveString("uuid", currentUuid); } return currentUuid; } static Future getAppVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); return int.parse(packageInfo.version.replaceAll(".0.0", "")); } static _printLongText(String text) { final pattern = RegExp('.{1,800}'); // 800 is the size of each chunk pattern.allMatches(text).forEach((match) => debugPrint(match.group(0))); } static void _persistLogMessage(String text, LogEntrySeverityEnum severity) async { var logEntry = LogEntry(); logEntry.time = DateTime.now(); logEntry.text = text; logEntry.severity = severity; await MyApp.isarInstance.writeTxn(() async { await MyApp.isarInstance.logEntrys.put(logEntry); }); } static logInfo(String message) { _printLongText(message); _persistLogMessage(message, LogEntrySeverityEnum.info); } static logDebug(String message) { _printLongText(message); _persistLogMessage(message, LogEntrySeverityEnum.debug); } static logException(String message) { _printLongText(message); _persistLogMessage(message, LogEntrySeverityEnum.error); } static int fastHash(String string) { var hash = 0xcbf29ce484222325; var i = 0; while (i < string.length) { final codeUnit = string.codeUnitAt(i++); hash ^= codeUnit >> 8; hash *= 0x100000001b3; hash ^= codeUnit & 0xFF; hash *= 0x100000001b3; } return hash; } static Widget getWaitWidget(context) { return ColoredSafeArea( key: const Key('value'), child: Container( width: 10, height: 10, color: Colors.white, ), ); } static Future fetchFutureIcon(icon) { return Future.delayed(const Duration(seconds: 0), () async { return icon; }); } static TextField getDefaultTextField(String hint, {TextEditingController? controller, FocusNode? focusNode, IconButton? iconButton, int? maxLines = 1, int? maxLength = 100, TextInputType? textInputType, List? formatter, Function()? onTap, bool? autofocus}) { iconButton ??= IconButton( icon: const Icon(Icons.clear), onPressed: () { if (controller != null) { controller.text = ""; } }, ); return TextField( controller: controller, focusNode: focusNode, maxLines: maxLines, onTap: onTap, keyboardType: textInputType, inputFormatters: formatter, maxLength: maxLength, autofocus: (autofocus != null) ? autofocus : false, decoration: InputDecoration( hintText: hint, contentPadding: const EdgeInsets.all(10), border: Util.getDefaultBorder(), errorBorder: Util.getDefaultBorder(), disabledBorder: Util.getDefaultBorder(), focusedBorder: Util.getDefaultFocusedBorder(), focusedErrorBorder: Util.getDefaultBorder(), suffixIcon: iconButton, labelText: hint), ); } static OutlineInputBorder getDefaultBorder() { return const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))); } static OutlineInputBorder getDefaultFocusedBorder() { return const OutlineInputBorder( borderSide: BorderSide(color: Constants.colorPrimary), borderRadius: BorderRadius.all(Radius.circular(20.0)), ); } static Future capturePhoto() async { final imagePicker = ImagePicker(); final pickedFile = await imagePicker.pickImage(source: ImageSource.camera); if (pickedFile != null) { return File(pickedFile.path); } return null; } static Future startBrowser(String websiteUrl) async { if (!await launchUrl(Uri.parse(websiteUrl))) { throw Exception('Could not launch $websiteUrl'); } } static Future playMp3(String name) async { final player = AudioPlayer(); await player.play(AssetSource('sounds/$name'), volume: 1); } } Widget getDefaultButton(String text, Function() onPressed, {Image? image, double? buttonHeight, bool enabledTimerActive = false, int enabledTimerDelay = 0}) { bool enabledState = !enabledTimerActive; return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { if (!enabledState) { Future.delayed(Duration(seconds: enabledTimerDelay), () { enabledState = true; if (context.mounted) { setState(() {}); } }); } return SizedBox( height: buttonHeight ?? 50, child: MaterialButton( color: const Color.fromARGB(255, 220, 220, 220), disabledColor: const Color.fromARGB(255, 220, 220, 220), disabledElevation: 5, elevation: 5, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(15), ), ), onPressed: () { if (enabledState) { Util.logInfo("Button \"$text\" clicked"); onPressed(); } }, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Visibility( visible: image != null, child: CircleAvatar( backgroundColor: Colors.transparent, child: image, ), ), Expanded( child: Opacity( opacity: enabledState ? 1.0 : 0.3, child: Text( text, style: const TextStyle(color: Colors.black, fontSize: 14.0), textAlign: TextAlign.center, ), ), ), ], ), ), ); }); } String mapVehicleTypeToImageName(int vehType) { switch (vehType) { case 1: return "fahrrad.png"; case 2: return "pkw.png"; case 3: return "kombi.png"; case 4: return "kasten.png"; case 5: return "bus.png"; case 6: return "grbus.png"; case 7: return "lkw.png"; case 8: return "lkw_12.png"; case 10: return "lkw_8.png"; case 11: return "lkw_10.png"; case 12: return "lkw_15.png"; case 13: return "lkw_20.png"; case 999: return "service.png"; } return "unbekannt.png"; } String mapVehicleTypeToText(int vehType) { switch (vehType) { case 1: return "Fahrrad"; case 2: return "Pkw"; case 3: return "Kombi"; case 4: return "Kasten"; case 5: return "Bus"; case 6: return "GrBus"; case 7: case 8: case 10: case 11: case 12: case 13: case 14: return "Lkw"; case 999: return "Service"; } return "!?????!"; } String mapTourMode(String mode) { var text = ""; switch (mode) { case "pu": text += "/ Abholung"; break; case "del": text += "/ Auslieferung"; break; case "pu/del": text += "/ Abholung/Auslieferung"; break; } return text; } Future showQuestionDialog(BuildContext context, String title, String message, List actions) async { await showDialog( context: context, builder: (context) { return AlertDialog( title: Text(title), content: Text(message), actions: actions, ); } ); } Future showAlertDialog(String title, String message) async { await showDialog( context: MyApp.appNavigatorKey.currentContext!, builder: (context) { return AlertDialog( title: Text(title), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('OK'), ), ], ); } ); } Future showMessageDialog(BuildContext context, String title, String message) async { await showDialog( barrierDismissible: false, context: context, builder: (context) { return AlertDialog( title: Text(title), content: Text(message), ); } ); } class DecimalTextInputFormatter extends TextInputFormatter { int maxLength; int decimalPlaces; DecimalTextInputFormatter({required this.maxLength, required this.decimalPlaces}); @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { var text = newValue.text; String? filteredText = ""; if (text != "") { RegExp regExp; if (decimalPlaces > 0) { regExp = RegExp(r'[0-9]+[,]{0,1}[0-9]{0,${' + decimalPlaces.toString() + '}'); } else { regExp = RegExp(r'[0-9]{0,' + maxLength.toString() +'}'); } filteredText = regExp.stringMatch(text); } return TextEditingValue(text: filteredText ?? oldValue.text); } } bool isJobInFuture(Job job) { return DateTime.parse(job.ordertime).add(Duration(minutes: job.offsWarn)).toUtc().millisecondsSinceEpoch > DateTime.now().toUtc().millisecondsSinceEpoch; } Future taskDialogSecurityException(BuildContext context) async { bool close = false; await showQuestionDialog(context, "Achtung", "Wenn Sie den Dialog jetzt schließen gehen erfasste Daten verloren und die Aufgabe oder Station bleibt unerledigt.", [ TextButton(onPressed: () => {Navigator.of(MyApp.appNavigatorKey.currentContext!).pop(), close = true}, child: const Text('Schließen')), TextButton(onPressed: () => {Navigator.of(MyApp.appNavigatorKey.currentContext!).pop(), close = false}, child: const Text('Zurück')), ]); if (close) { Navigator.of(MyApp.appNavigatorKey.currentContext!).pop(); } return close; }