Fix background service, add support for NBT instead of Json
This commit is contained in:
parent
a686412ec7
commit
4a8d515f4d
6 changed files with 289 additions and 24 deletions
274
lib/data.dart
274
lib/data.dart
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
|
@ -8,9 +9,19 @@ import 'package:floating_window_android/floating_window_android.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_background/flutter_background.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:libac_dart/nbt/NbtIo.dart';
|
||||
import 'package:libac_dart/nbt/NbtUtils.dart';
|
||||
import 'package:libac_dart/nbt/SnbtIo.dart';
|
||||
import 'package:libac_dart/nbt/Stream.dart';
|
||||
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
||||
import 'package:libac_dart/nbt/impl/DoubleTag.dart';
|
||||
import 'package:libac_dart/nbt/impl/ListTag.dart';
|
||||
import 'package:libac_dart/nbt/impl/StringTag.dart';
|
||||
import 'package:libac_dart/utils/Converter.dart';
|
||||
import 'package:libac_dart/utils/TimeUtils.dart';
|
||||
import 'package:libacflutter/nbt/nbtHelpers.dart';
|
||||
import 'package:timetrack/consts.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
||||
class SessionData {
|
||||
static DateTime StartTime = DateTime(0);
|
||||
|
@ -163,10 +174,14 @@ class SessionData {
|
|||
"Background notification for keeping TimeTrack running in the background",
|
||||
notificationImportance: AndroidNotificationImportance.normal,
|
||||
);
|
||||
|
||||
bool success = await FlutterBackground.initialize(
|
||||
androidConfig: androidConfig,
|
||||
);
|
||||
|
||||
FlutterBackground.enableBackgroundExecution();
|
||||
WakelockPlus.enable();
|
||||
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
@ -214,6 +229,7 @@ class SessionData {
|
|||
return;
|
||||
}
|
||||
positions.add(SmallPosition.fromPosition(pos));
|
||||
SessionData.SaveCacheState();
|
||||
|
||||
SessionData.Calls.dispatch();
|
||||
});
|
||||
|
@ -229,27 +245,167 @@ class SessionData {
|
|||
|
||||
EndTime = DateTime.now();
|
||||
|
||||
var saveData = SaveData();
|
||||
print(saveData);
|
||||
FlutterBackground.disableBackgroundExecution();
|
||||
WakelockPlus.disable();
|
||||
|
||||
var saveData = await _serializeToNBT();
|
||||
ResetAppSession();
|
||||
print(SnbtIo.writeToString(saveData));
|
||||
Uint8List nbtData = await NbtIo.writeToStream(saveData);
|
||||
|
||||
Trips = [];
|
||||
positions = [];
|
||||
|
||||
Dio dio = Dio();
|
||||
Map<String, dynamic> payload = {"cmd": "create", "data": saveData};
|
||||
_upload(nbtData);
|
||||
}
|
||||
|
||||
var reply = await dio.post(
|
||||
TTConsts.SESSION_SERVER,
|
||||
data: json.encode(payload),
|
||||
);
|
||||
Map<String, dynamic> replyJs = json.decode(reply.data as String);
|
||||
if (replyJs["status"] == "ok") {
|
||||
print("Successful upload");
|
||||
LastSessionID = replyJs['session'] as String;
|
||||
static Future<void> _upload(List<int> nbtData) async {
|
||||
Dio dio = Dio();
|
||||
|
||||
Map<String, dynamic> payload = {
|
||||
"cmd": "create",
|
||||
"data": base64Encoder.encode(nbtData),
|
||||
};
|
||||
|
||||
try {
|
||||
var reply = await dio.post(
|
||||
TTConsts.SESSION_SERVER,
|
||||
data: json.encode(payload),
|
||||
);
|
||||
if (reply.statusCode == null) {
|
||||
throw Exception("Fatal error while uploading");
|
||||
}
|
||||
if (reply.statusCode! != 200) {
|
||||
throw Exception("Fatal error while uploading");
|
||||
}
|
||||
|
||||
Map<String, dynamic> replyJs = json.decode(reply.data as String);
|
||||
if (replyJs["status"] == "ok") {
|
||||
print("Successful upload");
|
||||
LastSessionID = replyJs['session'] as String;
|
||||
Calls.dispatch();
|
||||
}
|
||||
} catch (E) {
|
||||
// Retry in 2 seconds
|
||||
DisplayError =
|
||||
"Error: Something went wrong during upload. Retry in 5 seconds...";
|
||||
Calls.dispatch();
|
||||
|
||||
Timer(Duration(seconds: 5), () {
|
||||
_upload(nbtData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetAppSession() {
|
||||
IsOnTheClock = false;
|
||||
StartTime = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
Trips = [];
|
||||
currentDelivery = null;
|
||||
currentTrip = null;
|
||||
positions = [];
|
||||
EndTime = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
LastSessionID = "";
|
||||
DisplayError = "";
|
||||
IsReadOnly = false;
|
||||
ContainsTripTimes = true;
|
||||
|
||||
Calls.dispatch();
|
||||
}
|
||||
|
||||
/// This function attempts to load the saved state from cache.
|
||||
///
|
||||
/// This will return true when a session exists; false when no session exists, or is empty.
|
||||
static Future<bool> LoadSavedCacheState() async {
|
||||
CompoundTag ct = await NBTHelper.GetNBT(name: "appstate");
|
||||
// Restore various flags now.
|
||||
if (ct.isEmpty) {
|
||||
ResetAppSession();
|
||||
return false;
|
||||
}
|
||||
|
||||
await _deserialize(ct);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Saves the current session based on various factors.
|
||||
static Future<void> SaveCacheState() async {
|
||||
CompoundTag ct = IsOnTheClock ? await _serializeToNBT() : CompoundTag();
|
||||
await NBTHelper.CommitNBT(data: ct, name: "appstate");
|
||||
}
|
||||
|
||||
static Future<void> _deserialize(CompoundTag ct) async {
|
||||
IsOnTheClock = NbtUtils.readBoolean(ct, "inprog");
|
||||
StartTime = DateTime.parse(ct.get("start")!.asString());
|
||||
if (ct.containsKey("end")) {
|
||||
EndTime = DateTime.parse(ct.get("end")!.asString());
|
||||
} else {
|
||||
EndTime = DateTime(0);
|
||||
}
|
||||
TotalPay = ct.get("totalPay")!.asDouble();
|
||||
|
||||
ListTag poses = ct.get("pos")! as ListTag;
|
||||
for (var pos in poses.value) {
|
||||
positions.add(await SmallPosition.fromNBT(pos.asCompoundTag()));
|
||||
}
|
||||
|
||||
ListTag trips = ct.get("trips") as ListTag;
|
||||
for (var trip in trips.value) {
|
||||
Trips.add(await Trip.fromNBT(trip.asCompoundTag()));
|
||||
}
|
||||
|
||||
if (ct.containsKey("current_trip")) {
|
||||
currentTrip = await Trip.fromNBT(ct.get("current_trip")!.asCompoundTag());
|
||||
}
|
||||
|
||||
if (ct.containsKey("current_delivery")) {
|
||||
currentDelivery = await Delivery.fromNBT(
|
||||
ct.get("current_delivery")!.asCompoundTag(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This private function will turn all the data into NBT, for both the cache state, and newer usage, for storing it on the server in a more compact format.
|
||||
static Future<CompoundTag> _serializeToNBT() async {
|
||||
CompoundTag ct = CompoundTag();
|
||||
|
||||
NbtUtils.writeBoolean(ct, "inprog", IsOnTheClock);
|
||||
// No need to write the contains trip times flag, it is set during deserialization. For inprog sessions, it will be set to true by the system.
|
||||
ct.put("start", StringTag.valueOf(StartTime.toIso8601String()));
|
||||
if (EndTime.year < 2000) {
|
||||
// We have a end time
|
||||
ct.put("end", StringTag.valueOf(EndTime.toIso8601String()));
|
||||
}
|
||||
|
||||
ListTag posX = ListTag();
|
||||
for (var pos in positions) {
|
||||
posX.add(await pos.toNBT());
|
||||
}
|
||||
ct.put("pos", posX);
|
||||
|
||||
ListTag myTrips = ListTag();
|
||||
for (var trip in Trips) {
|
||||
myTrips.add(await trip.toNBT());
|
||||
}
|
||||
ct.put("trips", myTrips);
|
||||
|
||||
// This format supports saving current trip and current delivery.
|
||||
if (currentDelivery != null) {
|
||||
ct.put("current_delivery", await currentDelivery!.toNBT());
|
||||
}
|
||||
|
||||
if (currentTrip != null) {
|
||||
ct.put("current_trip", await currentTrip!.toNBT());
|
||||
}
|
||||
|
||||
if (TotalPay != null) {
|
||||
ct.put("totalPay", DoubleTag.valueOf(TotalPay!));
|
||||
}
|
||||
|
||||
return ct;
|
||||
}
|
||||
|
||||
static Map<String, dynamic> SaveData() {
|
||||
Map<String, dynamic> saveData = {};
|
||||
|
||||
|
@ -283,7 +439,18 @@ class SessionData {
|
|||
data: json.encode(payload),
|
||||
);
|
||||
|
||||
return LoadData(reply.data as Map<String, dynamic>);
|
||||
String cType = reply.headers.value("Content-Type") ?? "application/json";
|
||||
|
||||
if (cType == "application/json") {
|
||||
return LoadData(reply.data as Map<String, dynamic>);
|
||||
} else if (cType == "application/nbt") {
|
||||
Uint8List lst = base64Encoder.decode(reply.data as String);
|
||||
// Convert this to a CompoundTag
|
||||
CompoundTag ct = await NbtIo.readFromStream(lst) as CompoundTag;
|
||||
_deserialize(ct);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} catch (E) {
|
||||
return false;
|
||||
}
|
||||
|
@ -389,7 +556,7 @@ class Delivery {
|
|||
|
||||
Map<String, dynamic> toJsonMap() {
|
||||
return {
|
||||
"start": StartTime.toString(),
|
||||
"start": StartTime.toIso8601String(),
|
||||
"endPos": endLocation?.toMap() ?? "incomplete",
|
||||
};
|
||||
}
|
||||
|
@ -407,6 +574,28 @@ class Delivery {
|
|||
|
||||
return delivery;
|
||||
}
|
||||
|
||||
Future<CompoundTag> toNBT() async {
|
||||
CompoundTag ct = CompoundTag();
|
||||
ct.put("start", StringTag.valueOf(StartTime.toIso8601String()));
|
||||
if (endLocation != null) {
|
||||
ct.put("endPos", await endLocation!.toNBT());
|
||||
}
|
||||
|
||||
return ct;
|
||||
}
|
||||
|
||||
static Future<Delivery> fromNBT(CompoundTag ct) async {
|
||||
Delivery delivery = Delivery();
|
||||
delivery.StartTime = DateTime.parse(ct.get("start")!.asString());
|
||||
if (ct.containsKey("endPos")) {
|
||||
delivery.endLocation = await SmallPosition.fromNBT(
|
||||
ct.get("endPos") as CompoundTag,
|
||||
);
|
||||
}
|
||||
|
||||
return delivery;
|
||||
}
|
||||
}
|
||||
|
||||
class Trip {
|
||||
|
@ -428,8 +617,8 @@ class Trip {
|
|||
|
||||
Map<String, dynamic> toJsonMap() {
|
||||
Map<String, Object> trip = {
|
||||
"start": StartTime.toString(),
|
||||
"end": EndTime.toString(),
|
||||
"start": StartTime.toIso8601String(),
|
||||
"end": EndTime.toIso8601String(),
|
||||
};
|
||||
List<Map<String, dynamic>> dropOffs = [];
|
||||
for (var delivery in deliveries) {
|
||||
|
@ -446,8 +635,9 @@ class Trip {
|
|||
trip.StartTime = DateTime.parse(jsx['start'] as String);
|
||||
if (jsx.containsKey("end")) {
|
||||
trip.EndTime = DateTime.parse(jsx['end'] as String);
|
||||
} else
|
||||
} else {
|
||||
SessionData.ContainsTripTimes = false;
|
||||
}
|
||||
trip.deliveries = [];
|
||||
List<dynamic> dropOffs = jsx['deliveries'] as List<dynamic>;
|
||||
|
||||
|
@ -457,6 +647,41 @@ class Trip {
|
|||
|
||||
return trip;
|
||||
}
|
||||
|
||||
Future<CompoundTag> toNBT() async {
|
||||
CompoundTag ct = CompoundTag();
|
||||
|
||||
ct.put("start", StringTag.valueOf(StartTime.toIso8601String()));
|
||||
if (EndTime.year < 2000) {
|
||||
ct.put("end", StringTag.valueOf(EndTime.toIso8601String()));
|
||||
}
|
||||
|
||||
ListTag drops = ListTag();
|
||||
for (var drop in deliveries) {
|
||||
drops.add(await drop.toNBT());
|
||||
}
|
||||
ct.put("deliveries", drops);
|
||||
|
||||
return ct;
|
||||
}
|
||||
|
||||
static Future<Trip> fromNBT(CompoundTag tag) async {
|
||||
Trip trip = Trip();
|
||||
|
||||
// Deserialize the Trip
|
||||
trip.StartTime = DateTime.parse(tag.get("start")!.asString());
|
||||
if (!tag.containsKey("end")) {
|
||||
SessionData.ContainsTripTimes = false;
|
||||
}
|
||||
|
||||
ListTag drops = tag.get("deliveries")! as ListTag;
|
||||
for (var drop in drops.value) {
|
||||
Delivery del = await Delivery.fromNBT(drop.asCompoundTag());
|
||||
trip.deliveries.add(del);
|
||||
}
|
||||
|
||||
return trip;
|
||||
}
|
||||
}
|
||||
|
||||
class Callbacks {
|
||||
|
@ -497,4 +722,19 @@ class SmallPosition {
|
|||
longitude: map['longitude'] as double,
|
||||
);
|
||||
}
|
||||
|
||||
Future<CompoundTag> toNBT() async {
|
||||
CompoundTag ct = CompoundTag();
|
||||
ct.put("latitude", DoubleTag.valueOf(latitude));
|
||||
ct.put("longitude", DoubleTag.valueOf(longitude));
|
||||
|
||||
return ct;
|
||||
}
|
||||
|
||||
static Future<SmallPosition> fromNBT(CompoundTag ct) async {
|
||||
return SmallPosition(
|
||||
latitude: ct.get("latitude")?.asDouble() ?? 0,
|
||||
longitude: ct.get("longitude")?.asDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue