diff --git a/lib/content_views/accepted_jobs_page.dart b/lib/content_views/accepted_jobs_page.dart index af5499e..fc77322 100644 --- a/lib/content_views/accepted_jobs_page.dart +++ b/lib/content_views/accepted_jobs_page.dart @@ -88,7 +88,7 @@ class _AcceptedJobsPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime)).toString(), + DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime).toLocal()).toString(), style: TextStyle(backgroundColor: isJobInFuture(job) ? Colors.yellow : Colors.transparent), textAlign: TextAlign.left, ), diff --git a/lib/content_views/assigned_jobs_page.dart b/lib/content_views/assigned_jobs_page.dart index 5b516e1..5d79a42 100644 --- a/lib/content_views/assigned_jobs_page.dart +++ b/lib/content_views/assigned_jobs_page.dart @@ -121,7 +121,7 @@ class _AssignedJobsPageState extends State { child: Column( children: [ Text( - DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime)).toString(), + DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime).toLocal()).toString(), textAlign: TextAlign.left, style: TextStyle(backgroundColor: isJobInFuture(job) ? Colors.yellow : Colors.white), ), diff --git a/lib/content_views/finished_jobs_page.dart b/lib/content_views/finished_jobs_page.dart index 7e1c1e6..a5a74ba 100644 --- a/lib/content_views/finished_jobs_page.dart +++ b/lib/content_views/finished_jobs_page.dart @@ -88,7 +88,7 @@ class _FinishedJobsPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime)).toString(), + DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime).toLocal()).toString(), textAlign: TextAlign.left, ), Text( diff --git a/lib/content_views/main_page.dart b/lib/content_views/main_page.dart index f285467..fe69bf3 100644 --- a/lib/content_views/main_page.dart +++ b/lib/content_views/main_page.dart @@ -230,7 +230,7 @@ class _MainPageState extends State { getDefaultButton( _loggedOut ? 'Anmelden' : 'Abmelden', image: Image.asset(_loggedOut ? 'assets/anmelden.png' : 'assets/abmelden.png', width: 30, height: 30), - () async => {_logout()}), + () async => {_logout(context)}), Expanded( child: Container( decoration: BoxDecoration( @@ -305,20 +305,58 @@ class _MainPageState extends State { } } - _logout() { + _logout(context) { if (!_loggedOut) { - _sendLogoutMessage(); - - Future.delayed(const Duration(seconds: 1), () async { - MqttCore.instance.diconnect(); - }); + showLogoutAlertDialog(context); } else { MqttCore.setup(); + + _loggedOut = false; + + setState(() {}); } + } - _loggedOut = !_loggedOut; + showLogoutAlertDialog(BuildContext context) { + Widget yesButton = TextButton( + child: const Text("Ja"), + onPressed: () { + Navigator.of(context).pop(); + _sendLogoutMessage(); - setState(() {}); + Future.delayed(const Duration(seconds: 1), () async { + MqttCore.instance.diconnect(); + }); + + _loggedOut = true; + + setState(() {}); + }, + ); + + Widget noButton = TextButton( + child: const Text("Nein"), + onPressed: () { + Navigator.of(context).pop(); + }, + ); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + title: const Text("Warnung"), + content: const Text("Wollen Sie sich wirklich abmelden?"), + actions: [ + yesButton, noButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); } _sendLogoutMessage() { diff --git a/lib/content_views/messages_page.dart b/lib/content_views/messages_page.dart index 0412971..fe1f225 100644 --- a/lib/content_views/messages_page.dart +++ b/lib/content_views/messages_page.dart @@ -6,6 +6,7 @@ import 'package:stadtbote/content_views/utils/content_view_widget.dart'; import 'package:stadtbote/content_views/utils/header_info.dart'; import 'package:stadtbote/main.dart'; import 'package:stadtbote/model/message.dart' as sbmessage; +import 'package:stadtbote/model/message.dart'; import 'package:stadtbote/model/session_status.dart'; import 'package:stadtbote/tools/mqtt_core.dart'; @@ -93,16 +94,17 @@ class _MessagesPageState extends State { void _handleSendPressed(types.PartialText text) async { var now = DateTime.now(); - var message = sbmessage.Message(); - message.text = [text.text]; - message.from = _sid; - message.subj = ''; - message.time = now.toIso8601String(); - message.read = true; - message.id = await sbmessage.Message.getNextId(); + var message = MessageMessage(); + message.mssg = sbmessage.Message(); + message.mssg.text = [text.text]; + message.mssg.from = _sid; + message.mssg.subj = ''; + message.mssg.time = now.toIso8601String(); + message.mssg.read = true; + message.mssg.id = await sbmessage.Message.getNextId(); MyApp.isarInstance.writeTxnSync(() { - MyApp.isarInstance.messages.putSync(message); + MyApp.isarInstance.messages.putSync(message.mssg); }); MqttCore.instance.publishPayloadMessage('mssg', message.toJson()); @@ -110,7 +112,7 @@ class _MessagesPageState extends State { _messages.insert(0, types.TextMessage( author: _user, createdAt: now.millisecondsSinceEpoch, - id: message.id.toString(), + id: message.mssg.id.toString(), text: text.text, )); diff --git a/lib/content_views/task_list_page.dart b/lib/content_views/task_list_page.dart index 5541986..2f11aeb 100644 --- a/lib/content_views/task_list_page.dart +++ b/lib/content_views/task_list_page.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:ffi'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -20,6 +21,7 @@ import 'package:stadtbote/popups/tasks_popups/take_photos_popup.dart'; import 'package:stadtbote/protocoll_messages/accept_job_message.dart'; import 'package:stadtbote/protocoll_messages/fin_task_message.dart'; import 'package:stadtbote/protocoll_messages/fin_tour_message.dart'; +import 'package:stadtbote/protocoll_messages/pic_message.dart'; import 'package:stadtbote/tools/constants.dart'; import 'package:stadtbote/tools/message_service.dart'; import 'package:stadtbote/tools/mqtt_core.dart'; @@ -64,6 +66,8 @@ class _TaskListPageState extends State { late List _taskList; + List _photos = []; + @override void initState() { Future.delayed(const Duration(seconds: 0), () async { @@ -282,6 +286,7 @@ class _TaskListPageState extends State { answer.codes!.add(barcode); } + taskInfo.answer = jsonEncode(answer.toJson()); taskInfo.additionalText = "Barcodes gescannt"; taskInfo.completed = true; @@ -292,13 +297,17 @@ class _TaskListPageState extends State { } _processTakePhotos(TaskInfo taskInfo) { - showTakePhotosPopup(context, taskInfo, () { - taskInfo.additionalText = "Fotos aufgenommen"; - taskInfo.completed = true; + showTakePhotosPopup(context, taskInfo, (List? photos) { + if (photos != null) { + _photos = photos; - setState(() {}); + taskInfo.additionalText = "Fotos aufgenommen"; + taskInfo.completed = true; - _finishTaskProcessing(taskInfo); + setState(() {}); + + _finishTaskProcessing(taskInfo); + } }); } @@ -349,23 +358,29 @@ class _TaskListPageState extends State { } void _processSignature(TaskInfo taskInfo) { + MyApp.suppressAcceptDialog = true; + SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]); Future.delayed(const Duration(seconds: 1), () async { - showSignaturePopup(context, taskInfo, () { - var answer = _createAnswerHeader(taskInfo); - answer.sign = 'A110'; - - taskInfo.answer = jsonEncode(answer.toJson()); - taskInfo.completed = true; + showSignaturePopup(context, taskInfo, (String? signature) { + MyApp.suppressAcceptDialog = false; SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - _sendFinTourMessage(); + if (signature != null) { + var answer = _createAnswerHeader(taskInfo); + answer.sign = signature; - _finishTaskProcessing(taskInfo); + taskInfo.answer = jsonEncode(answer.toJson()); + taskInfo.completed = true; - MessageService().publish(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 2)); + _sendFinTourMessage(); + + _finishTaskProcessing(taskInfo); + + MessageService().publish(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 2)); + } }); }); } @@ -457,5 +472,84 @@ class _TaskListPageState extends State { await MyApp.isarInstance.writeTxn(() async { await MyApp.isarInstance.jobs.put(widget.job); }); + + _sendImages(); } + + _sendImages() { + Future.delayed(const Duration(seconds: 1), () { + if (_photos.isNotEmpty) { + int photoIndex = 0; + + for (var photoData in _photos) { + var message = PicMessage(); + message.pic = PicData(); + message.pic.trId = widget.tour.id; + message.pic.num = photoIndex; + message.pic.time = DateTime.now().millisecondsSinceEpoch; + message.pic.data = base64Encode(photoData); + + var position = MyApp.currentPosition; + if (position != null) { + message.pos = PicMessagePos(); + message.pos.lat = position.latitude; + message.pos.long = position.longitude; + } + + MqttCore.instance.publishPayloadMessage("job", message.toJson()); + } + } + }); + } + + /*private void SendImages() + { + var tourId = _jobTour.Id; + + Task.Run(() => + { + try + { + Thread.Sleep(1000); + + var index = 0; + var realm = Realm.GetInstance(); + var tourImages = realm.All().Where(tourImage => tourImage.TourId == tourId); + if (tourImages.Any()) + { + foreach (var tourImage in tourImages) + { + var payloadObject = new JObject(); + + var picObject = new JObject(); + payloadObject.Add(new JProperty("pic", picObject)); + + picObject.Add(new JProperty("tr_id", tourImage.TourId)); + picObject.Add(new JProperty("num", index)); + picObject.Add(new JProperty("time", DateTime.Now.ToString("o"))); + picObject.Add(new JProperty("data", tourImage.ImageData)); + + var location = App.CurrentLocation; + if (location != null) + { + var posObject = new JObject(); + picObject.Add(new JProperty("pos", posObject)); + + posObject.Add(new JProperty("lat", location.Latitude)); + posObject.Add(new JProperty("long", location.Longitude)); + } + + App.Core.SendJsonMessageAsync("job", payloadObject); + + index++; + } + + Thread.Sleep(500); + } + } + catch (Exception ex) + { + } + }); + }*/ } diff --git a/lib/main.dart b/lib/main.dart index a3f74ad..4f411b2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -60,6 +60,8 @@ class MyApp extends StatelessWidget { static Position? currentPosition; + static bool suppressAcceptDialog = false; + @override Widget build(BuildContext context) { return MaterialApp( @@ -417,8 +419,10 @@ class _MainState extends State
with SingleTickerProviderStateMixin { } void _jobReceived(Job job) async { - if (job.state == 0) { - await showAcceptJobsPopup(context, await MyApp.isarInstance.jobs.filter().stateEqualTo(0).findAll()); + if (!MyApp.suppressAcceptDialog) { + if (job.state == 0) { + await showAcceptJobsPopup(context, await MyApp.isarInstance.jobs.filter().stateEqualTo(0).findAll()); + } } } } diff --git a/lib/model/message.dart b/lib/model/message.dart index 20500aa..873154b 100644 --- a/lib/model/message.dart +++ b/lib/model/message.dart @@ -5,6 +5,17 @@ import 'package:stadtbote/tools/util.dart'; part 'message.g.dart'; +@JsonSerializable() +class MessageMessage { + late Message mssg; + + MessageMessage(); + + factory MessageMessage.fromJson(Map json) => _$MessageMessageFromJson(json); + + Map toJson() => _$MessageMessageToJson(this); +} + @collection @JsonSerializable() class Message { diff --git a/lib/model/message.g.dart b/lib/model/message.g.dart index 4a485f7..8cec015 100644 --- a/lib/model/message.g.dart +++ b/lib/model/message.g.dart @@ -1098,6 +1098,15 @@ extension MessageQueryProperty // JsonSerializableGenerator // ************************************************************************** +MessageMessage _$MessageMessageFromJson(Map json) => + MessageMessage() + ..mssg = Message.fromJson(json['mssg'] as Map); + +Map _$MessageMessageToJson(MessageMessage instance) => + { + 'mssg': instance.mssg, + }; + Message _$MessageFromJson(Map json) => Message() ..id = json['id'] as int ..time = json['time'] as String diff --git a/lib/popups/accept_jobs_popup.dart b/lib/popups/accept_jobs_popup.dart index ed8d031..2937482 100644 --- a/lib/popups/accept_jobs_popup.dart +++ b/lib/popups/accept_jobs_popup.dart @@ -78,7 +78,7 @@ Future showAcceptJobsPopup(BuildContext context, List jobs) async { SizedBox( width: double.infinity, child: Text( - DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime)).toString(), + DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime).toLocal()).toString(), textAlign: TextAlign.left, style: TextStyle(backgroundColor: isJobInFuture(job) ? Colors.yellow : Colors.transparent), ), diff --git a/lib/popups/tasks_popups/receipt_giver_popup.dart b/lib/popups/tasks_popups/receipt_giver_popup.dart index a303387..15d9044 100644 --- a/lib/popups/tasks_popups/receipt_giver_popup.dart +++ b/lib/popups/tasks_popups/receipt_giver_popup.dart @@ -40,10 +40,15 @@ showReceiptGiverPopup(BuildContext context, TaskInfo taskInfo, Function(String) controller: textEditingController, autofocus: true), const SizedBox(height: 20), - getDefaultButton('Übernehmen', () => { - Navigator.of(context).pop(), + getDefaultButton('Übernehmen', () { + if (textEditingController.text.isNotEmpty) { + Navigator.of(context).pop(); - onFinished(textEditingController.text)}) + onFinished(textEditingController.text); + } else { + showAlertDialog(context, "Fehler", "Sie müssen einen Text eingeben"); + } + }) ], ), ), diff --git a/lib/popups/tasks_popups/signature_popup.dart b/lib/popups/tasks_popups/signature_popup.dart index fd484d3..d6ad731 100644 --- a/lib/popups/tasks_popups/signature_popup.dart +++ b/lib/popups/tasks_popups/signature_popup.dart @@ -1,13 +1,22 @@ import 'package:flutter/material.dart'; import 'package:signature/signature.dart'; +import 'package:stadtbote/main.dart'; import 'package:stadtbote/model/task_info.dart'; import 'package:stadtbote/tools/util.dart'; -showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function() onFinished) async { +showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function(String?) onFinished) async { await showDialog( context: context, builder: (BuildContext context) { - return AlertDialog( + return WillPopScope( + onWillPop: () async { + Navigator.of(context).pop(); + + onFinished(null); + + return true; + },// False will prevent and true will allow to dismiss + child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, backgroundColor: Colors.white, @@ -18,6 +27,25 @@ showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function() onFinishe var popupHeight = MediaQuery.of(context).size.height * 0.85; var popupWidth = MediaQuery.of(context).size.width * 0.85; + String convertPoints(List points) { + bool firstTap = true; + if (points.isNotEmpty) { + String result = ""; + + for (var point in points) { + if (point.type != PointType.tap) { + result += "${point.offset.dx.toInt()},${point.offset.dy.toInt()};"; + } else { + result += "A"; + } + } + + return result.replaceAll("AA", "A"); + } + + return ""; + } + return SizedBox( key: GlobalKey(), height: popupHeight, @@ -48,12 +76,13 @@ showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function() onFinishe getDefaultButton('Übernehmen', buttonHeight: 50, () => { Navigator.of(context).pop(), - onFinished()}) + onFinished(convertPoints(controller.points)) + }) ], ), ), )); }, - )); + ))); }); } diff --git a/lib/popups/tasks_popups/take_photos_popup.dart b/lib/popups/tasks_popups/take_photos_popup.dart index 88af5b4..49f6312 100644 --- a/lib/popups/tasks_popups/take_photos_popup.dart +++ b/lib/popups/tasks_popups/take_photos_popup.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:stadtbote/model/task_info.dart'; import 'package:stadtbote/tools/util.dart'; -Future showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Function() onFinished) async { +Future showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Function(List) onFinished) async { List imageDataList = []; List imageWidgetsList = []; @@ -90,7 +90,7 @@ Future showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Functi buttonHeight: 50, () => { if (imageDataList.length == taskInfo.task.min! && imageDataList.length <= taskInfo.task.max!) - {Navigator.of(context).pop(), onFinished()} + {Navigator.of(context).pop(), onFinished(imageDataList)} else {showAlertDialog(context, 'Fehler', 'Sie müssen mindestens ${taskInfo.task.min} Foto(s) aufnehmen')} }) diff --git a/lib/protocoll_messages/pic_message.dart b/lib/protocoll_messages/pic_message.dart new file mode 100644 index 0000000..03b66ef --- /dev/null +++ b/lib/protocoll_messages/pic_message.dart @@ -0,0 +1,50 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'pic_message.g.dart'; + +@JsonSerializable() +class PicMessage { + late PicData pic; + + late PicMessagePos pos; + + PicMessage(); + + factory PicMessage.fromJson(Map json) => _$PicMessageFromJson(json); + + Map toJson() => _$PicMessageToJson(this); +} + +@JsonSerializable() +class PicData +{ + @JsonKey(name: "tr_id") + late int trId; + + late int num; + + late int time; + + late String data; + + PicData(); + + factory PicData.fromJson(Map json) => _$PicDataFromJson(json); + + Map toJson() => _$PicDataToJson(this); +} + +@JsonSerializable() +class PicMessagePos { + late double lat; + + late double long; + + late int acc = 0; + + PicMessagePos(); + + factory PicMessagePos.fromJson(Map json) => _$PicMessagePosFromJson(json); + + Map toJson() => _$PicMessagePosToJson(this); +} \ No newline at end of file diff --git a/lib/protocoll_messages/pic_message.g.dart b/lib/protocoll_messages/pic_message.g.dart new file mode 100644 index 0000000..ad0e814 --- /dev/null +++ b/lib/protocoll_messages/pic_message.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'pic_message.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PicMessage _$PicMessageFromJson(Map json) => PicMessage() + ..pic = PicData.fromJson(json['pic'] as Map) + ..pos = PicMessagePos.fromJson(json['pos'] as Map); + +Map _$PicMessageToJson(PicMessage instance) => + { + 'pic': instance.pic, + 'pos': instance.pos, + }; + +PicData _$PicDataFromJson(Map json) => PicData() + ..trId = json['tr_id'] as int + ..num = json['num'] as int + ..time = json['time'] as int + ..data = json['data'] as String; + +Map _$PicDataToJson(PicData instance) => { + 'tr_id': instance.trId, + 'num': instance.num, + 'time': instance.time, + 'data': instance.data, + }; + +PicMessagePos _$PicMessagePosFromJson(Map json) => + PicMessagePos() + ..lat = (json['lat'] as num).toDouble() + ..long = (json['long'] as num).toDouble() + ..acc = json['acc'] as int; + +Map _$PicMessagePosToJson(PicMessagePos instance) => + { + 'lat': instance.lat, + 'long': instance.long, + 'acc': instance.acc, + }; diff --git a/lib/tools/mqtt_core.dart b/lib/tools/mqtt_core.dart index b987b26..957daf3 100644 --- a/lib/tools/mqtt_core.dart +++ b/lib/tools/mqtt_core.dart @@ -198,7 +198,9 @@ class MqttCore { break; case 'job_del': - _jobDeleteReceived(payloadData); + if (!kDebugMode) { + _jobDeleteReceived(payloadData); + } break; case 'sync_request':