Do some refactoring for protocol v2
This commit is contained in:
parent
d9c79a4ee9
commit
8adaf6169a
5 changed files with 122 additions and 3 deletions
|
@ -9,7 +9,7 @@ class TTConsts {
|
||||||
static get SESSION_SERVER =>
|
static get SESSION_SERVER =>
|
||||||
"https://api.zontreck.com/timetrack/$UPDATE_CHANNEL/timetrack.php";
|
"https://api.zontreck.com/timetrack/$UPDATE_CHANNEL/timetrack.php";
|
||||||
|
|
||||||
static const VERSION = "1.0.0-beta.31";
|
static const VERSION = "1.0.0-beta.32";
|
||||||
|
|
||||||
static bool UPDATE_AVAILABLE = false;
|
static bool UPDATE_AVAILABLE = false;
|
||||||
static UpdateChannel UPDATE_CHANNEL = UpdateChannel.beta;
|
static UpdateChannel UPDATE_CHANNEL = UpdateChannel.beta;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
@ -45,6 +46,12 @@ class SessionData {
|
||||||
static bool IsSavedData = false;
|
static bool IsSavedData = false;
|
||||||
static String SaveDataType = "";
|
static String SaveDataType = "";
|
||||||
|
|
||||||
|
/// This indicates whether the app is in a live session.
|
||||||
|
static bool Recording = false;
|
||||||
|
|
||||||
|
/// This is the version number of the recording as specified by the server.
|
||||||
|
static int RecordingVersion = 0;
|
||||||
|
|
||||||
/// Is true if the try-catch is tripped or if not running on Android
|
/// Is true if the try-catch is tripped or if not running on Android
|
||||||
static bool isWeb = false;
|
static bool isWeb = false;
|
||||||
|
|
||||||
|
@ -236,6 +243,8 @@ class SessionData {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _create();
|
||||||
|
|
||||||
_listener = Geolocator.getPositionStream(
|
_listener = Geolocator.getPositionStream(
|
||||||
locationSettings: TTConsts.LOCATION_SETTINGS,
|
locationSettings: TTConsts.LOCATION_SETTINGS,
|
||||||
).listen((pos) {
|
).listen((pos) {
|
||||||
|
@ -274,6 +283,50 @@ class SessionData {
|
||||||
_upload(nbtData);
|
_upload(nbtData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// v2 Create function.
|
||||||
|
///
|
||||||
|
/// This function sets the Session ID globally. It will also set the Recording flag to true.
|
||||||
|
static Future<void> _create() async {
|
||||||
|
Dio dio = Dio();
|
||||||
|
Map<String, dynamic> payload = {"cmd": "createv2"};
|
||||||
|
|
||||||
|
try {
|
||||||
|
var reply = await dio.post(
|
||||||
|
TTConsts.SESSION_SERVER,
|
||||||
|
data: json.encode(payload),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (reply.statusCode == null) {
|
||||||
|
throw Exception("Fatal error while creating");
|
||||||
|
}
|
||||||
|
if (reply.statusCode! != 200) {
|
||||||
|
throw Exception("Fatal error while creating");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> replyJs = json.decode(reply.data as String);
|
||||||
|
if (replyJs["status"] == "created") {
|
||||||
|
print("Successful creation");
|
||||||
|
LastSessionID = replyJs['session'] as String;
|
||||||
|
Recording = true;
|
||||||
|
RecordingVersion = 0;
|
||||||
|
Calls.dispatch();
|
||||||
|
}
|
||||||
|
} catch (E) {
|
||||||
|
// Retry in 2 seconds
|
||||||
|
DisplayError =
|
||||||
|
"Error: Something went wrong during session creation. Retry in 5 seconds...";
|
||||||
|
Calls.dispatch();
|
||||||
|
|
||||||
|
Timer(Duration(seconds: 5), () {
|
||||||
|
_create();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deprecated: This function uploads using the v1 API, and is intended to be called at the conclusion of data recording.
|
||||||
|
@Deprecated(
|
||||||
|
"This function utilizes the Protocol v1 Create method, which has been replaced by createv2 and patch. This function will be removed.",
|
||||||
|
)
|
||||||
static Future<void> _upload(List<int> nbtData) async {
|
static Future<void> _upload(List<int> nbtData) async {
|
||||||
Dio dio = Dio();
|
Dio dio = Dio();
|
||||||
|
|
||||||
|
@ -325,6 +378,7 @@ class SessionData {
|
||||||
DisplayError = "";
|
DisplayError = "";
|
||||||
IsReadOnly = false;
|
IsReadOnly = false;
|
||||||
ContainsTripTimes = true;
|
ContainsTripTimes = true;
|
||||||
|
Recording = false;
|
||||||
if (FlutterBackground.isBackgroundExecutionEnabled) {
|
if (FlutterBackground.isBackgroundExecutionEnabled) {
|
||||||
FlutterBackground.disableBackgroundExecution();
|
FlutterBackground.disableBackgroundExecution();
|
||||||
}
|
}
|
||||||
|
@ -367,6 +421,15 @@ class SessionData {
|
||||||
|
|
||||||
static Future<void> _deserialize(CompoundTag ct) async {
|
static Future<void> _deserialize(CompoundTag ct) async {
|
||||||
IsOnTheClock = NbtUtils.readBoolean(ct, "inprog");
|
IsOnTheClock = NbtUtils.readBoolean(ct, "inprog");
|
||||||
|
if (ct.containsKey("record"))
|
||||||
|
Recording = NbtUtils.readBoolean(ct, "record");
|
||||||
|
else
|
||||||
|
Recording = false;
|
||||||
|
|
||||||
|
if (Recording && isWeb) {
|
||||||
|
IsOnTheClock = false;
|
||||||
|
IsReadOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsOnTheClock) {
|
if (IsOnTheClock) {
|
||||||
await Login();
|
await Login();
|
||||||
|
@ -416,6 +479,7 @@ class SessionData {
|
||||||
CompoundTag ct = CompoundTag();
|
CompoundTag ct = CompoundTag();
|
||||||
|
|
||||||
NbtUtils.writeBoolean(ct, "inprog", IsOnTheClock);
|
NbtUtils.writeBoolean(ct, "inprog", IsOnTheClock);
|
||||||
|
NbtUtils.writeBoolean(ct, "record", Recording);
|
||||||
// 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.
|
// 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()));
|
ct.put("start", StringTag.valueOf(StartTime.toIso8601String()));
|
||||||
if (EndTime.year > 2000) {
|
if (EndTime.year > 2000) {
|
||||||
|
@ -603,6 +667,29 @@ class SessionData {
|
||||||
|
|
||||||
return tm.toString();
|
return tm.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<int> FetchVersion() async {
|
||||||
|
Dio dio = Dio();
|
||||||
|
var packet = json.encode({"cmd": "get_version", "id": LastSessionID});
|
||||||
|
|
||||||
|
var response = await dio.post(TTConsts.SESSION_SERVER, data: packet);
|
||||||
|
if (response.statusCode == null) {
|
||||||
|
DisplayError = "Error: Version retrieval failed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (response.statusCode! != 200) {
|
||||||
|
DisplayError = "Error: Session server HTTP Response code";
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply = json.decode(response.data as String);
|
||||||
|
if (reply["status"] == "version_back") {
|
||||||
|
return reply["version"] as int;
|
||||||
|
} else {
|
||||||
|
DisplayError = "Error: Unknown get_version reply";
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Delivery {
|
class Delivery {
|
||||||
|
|
|
@ -411,7 +411,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"You are now on the clock\nYour location is being tracked for record keeping purposes.\n\nYou started ${SessionData.GetTotalTimeWorked(SessionData.StartTime, DateTime.now())} ago\n\n",
|
"Your location is being tracked for record keeping purposes.\n\nYou started ${SessionData.GetTotalTimeWorked(SessionData.StartTime, DateTime.now())} ago\n\n",
|
||||||
style: TextStyle(fontSize: 18),
|
style: TextStyle(fontSize: 18),
|
||||||
),
|
),
|
||||||
if (SessionData.currentTrip != null) GetTripWidgets(),
|
if (SessionData.currentTrip != null) GetTripWidgets(),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:libacflutter/Constants.dart';
|
import 'package:libacflutter/Constants.dart';
|
||||||
import 'package:timetrack/consts.dart';
|
import 'package:timetrack/consts.dart';
|
||||||
|
@ -19,9 +21,31 @@ class _WebMain extends State<WebMain> {
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
sessionIDController.text = SessionData.LastSessionID;
|
sessionIDController.text = SessionData.LastSessionID;
|
||||||
|
|
||||||
|
// Check if FirstRun
|
||||||
|
if (SessionData.Calls.HomeCallback == null) {
|
||||||
|
SessionData.Calls.HomeCallback = _callback;
|
||||||
|
// After doing this, we also want to schedule the timer
|
||||||
|
Timer.periodic(Duration(seconds: 5), (timer) async {
|
||||||
|
if (!SessionData.Recording) {
|
||||||
|
timer.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the latest version number, compare, then redownload the data.
|
||||||
|
int ver = await SessionData.FetchVersion();
|
||||||
|
if (ver != SessionData.RecordingVersion) {
|
||||||
|
await SessionData.DownloadData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _callback() async {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -132,6 +156,14 @@ class _WebMain extends State<WebMain> {
|
||||||
),
|
),
|
||||||
tileColor: const Color.fromARGB(255, 7, 123, 255),
|
tileColor: const Color.fromARGB(255, 7, 123, 255),
|
||||||
),
|
),
|
||||||
|
if (SessionData.Recording)
|
||||||
|
ListTile(
|
||||||
|
title: Text("LIVE SESSION"),
|
||||||
|
subtitle: Text(
|
||||||
|
"This session is live! Recording is still in progress. Over time this live view will automatically refresh until the recording is ended.\n\nSession Version: ${SessionData.RecordingVersion}",
|
||||||
|
),
|
||||||
|
tileColor: LibACFlutterConstants.TITLEBAR_COLOR,
|
||||||
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await showDialog(
|
await showDialog(
|
||||||
|
|
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.0-beta.31
|
version: 1.0.0-beta.32
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue