Merge branch 'master' into bug3932

This commit is contained in:
Axel Uhl committed 2016-09-29 16:58:50 +02:00
commit a2fdddcd83
18 files changed
+198 -44

No files matched your search

@@ -65,7 +65,7 @@ public abstract class BaseRegattaLogDeviceMappingFinder<ItemT extends WithID>
if (!endOfFirstHalf.before(from)) {
log.add(createDeviceMappingEvent(item, event.getAuthor(), from, endOfFirstHalf, event.getDevice()));
}
if (!to.before(startOfSecondHalf)) {
if (to == null || !to.before(startOfSecondHalf)) {
log.add(createDeviceMappingEvent(item, event.getAuthor(), startOfSecondHalf, to,
event.getDevice()));
}
@@ -17,8 +17,12 @@ public class FlatGPSFixJsonSerializer implements JsonSerializer<GPSFix> {
result.put(FIELD_LON_DEG, object.getPosition().getLngDeg());
result.put(FIELD_LAT_DEG, object.getPosition().getLatDeg());
result.put(FIELD_TIME_MILLIS, object.getTimePoint().asMillis());
// TODO the default accuracy is 1.0; when fixes ever have an accuracy, report it here
result.put(FIELD_ACCURACY, 1.0);
// TODO Insert Accuracy if someday available from the server
// Buoy Pinger app will interpret no accuracy in JSON message as MarkPingInfo.NOT_SET_BY_USER (-1) and store that in DB
// For the user these values are displayed as R.string.unknown ("n/a");
// (CheckinManager in Buoy Pinger can accept messages with and without accuracy)
// result.put(FIELD_ACCURACY, XXXXXXXXX);
return result;
}
}
@@ -1,11 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="settings_category_energy_saving">Batterie Sparen</string>
<string name="energy_saving_enabled">Batteriesparmodus</string>
<string name="preference_energy_saving_is_enabled">Ein</string>
<string name="preference_energy_saving_is_disabled">Aus</string>
<string name="preference_energy_saving_summary_is_enabled">GPS Updates werden alle 30 Sekunden verschickt.</string>
<string name="preference_energy_saving_summary_is_disabled">GPS Updates werden jede Sekunde verschickt.</string>
<string name="preference_energy_saving_sending_interval">Sendeintervall für GPS-Updates auswählen</string>
<string name="preference_energy_saving_sending_interval_summary">Wählen Sie den größtmöglichen Wert, der dennoch eine dem Anwendungsfall entsprechend hohe Aktualisierungsrate ermöglicht.</string>
<string name="preference_energy_saving_enabled_automatically">Der Energiesparmodus wurde automatisch aktiviert, GPS Updates werden alle 20 Sekunden an den Server gesendet. Diese Einstellung wird überschrieben, wenn Sie oben ein anderes Intervall auswählen.</string>
<string name="preference_energy_saving_sending_interval_message">GPS Updates werden%1$s %2$s an den Server gesendet.</string>
<string name="preference_energy_saving_sending_interval_message_every">alle</string>
<string-array name="preference_energy_saving_sending_interval_entries">
<item>sofort</item>
<item>5 Sekunden</item>
<item>10 Sekunden</item>
<item>20 Sekunden</item>
<item>60 Sekunden</item>
</string-array>
<string name="preference_heading_with_declination_subtracted_is_enabled">Ein</string>
<string name="preference_heading_with_declination_subtracted_is_disabled">Aus</string>
<string name="preference_heading_with_declination_subtracted_summary_is_enabled">Anzeige: Missweisend</string>
@@ -78,5 +78,6 @@
<string name="upload_in_progress">Wird hochgeladen&#8230;</string>
<string name="upload_io_error">Fehler beim Hochladen: </string>
<string name="upload_retry">Fehler beim Hochladen.\nZum Wiederholen hier tippen.</string>
<string name="tracking_status_gps_found">GPS Signal gefunden!</string>
</resources>
@@ -7,14 +7,34 @@
<string name="app_folder" translatable="false">tracking</string>
<string name="preference_energy_saving_enabled_key" translatable="false">preferenceEnergySavingEnabled</string>
<string name="preference_energy_saving_override_key" translatable="false">preferenceEnergySavingOverride</string>
<string name="preference_heading_with_declination_subtracted_key" translatable="false">preferenceHeadingWithDeclinationSubtracted</string>
<string name="settings_category_energy_saving">Battery Saving</string>
<string name="energy_saving_enabled">Save Energy</string>
<string name="preference_energy_saving_is_enabled">On</string>
<string name="preference_energy_saving_is_disabled">Off</string>
<string name="preference_energy_saving_summary_is_enabled">GPS updates are being sent to server every 30 seconds.</string>
<string name="preference_energy_saving_summary_is_disabled">GPS updates are being sent to server every second.</string>
<string name="preference_energy_saving_sending_interval_key" translatable="false">msgSndInt</string>
<string name="preference_energy_saving_sending_interval_default" translatable="false">0</string>
<string name="preference_energy_saving_sending_interval">choose GPS update sending interval</string>
<string name="preference_energy_saving_sending_interval_summary">Choose a value as high as possible to preserve battery life while still maintaining a reasonable live experience, depending on your use case.</string>
<string name="preference_energy_saving_enabled_automatically">Energy saving mode has been enabled automatically, GPS updates are being sent to the server every 20 seconds. You can change that by selecting an interval above.</string>
<string name="preference_energy_saving_sending_interval_message">GPS updates will be sent to the server%1$s %2$s.</string>
<string name="preference_energy_saving_sending_interval_message_key">msgSndIntMessage</string>
<string name="preference_energy_saving_sending_interval_message_every">every</string>
<string-array name="preference_energy_saving_sending_interval_entries">
<item>immediately</item>
<item>5 seconds</item>
<item>10 seconds</item>
<item>20 seconds</item>
<item>60 seconds</item>
</string-array>
<string-array name="preference_energy_saving_sending_interval_values" translatable="false">
<item>0</item>
<item>5</item>
<item>10</item>
<item>20</item>
<item>60</item>
</string-array>
<string name="preference_gps_fix_interval_ms_key" translatable="false">gpsFixInterval</string>
<string name="preference_gps_fix_interval_ms_default" translatable="false">500</string>
@@ -19,4 +19,5 @@
<string name="tracking_status_no_gps_signal">No GPS Signal!</string>
<string name="desc_regatta_user_image">Regatta User Image</string>
<string name="desc_add_photo_button">Add Photo Button</string>
<string name="tracking_status_gps_found">GPS signal found!</string>
</resources>
@@ -3,14 +3,17 @@
<!-- Energy Saving -->
<PreferenceCategory android:title="@string/settings_category_energy_saving">
<SwitchPreference
android:defaultValue="false"
android:key="@string/preference_energy_saving_enabled_key"
android:switchTextOn="@string/preference_energy_saving_is_enabled"
android:switchTextOff="@string/preference_energy_saving_is_disabled"
android:summaryOn="@string/preference_energy_saving_summary_is_enabled"
android:summaryOff="@string/preference_energy_saving_summary_is_disabled"
android:title="@string/energy_saving_enabled"/>
<ListPreference
android:defaultValue="@string/preference_energy_saving_sending_interval_default"
android:key="@string/preference_energy_saving_sending_interval_key"
android:entryValues="@array/preference_energy_saving_sending_interval_values"
android:entries="@array/preference_energy_saving_sending_interval_entries"
android:title="@string/preference_energy_saving_sending_interval"
android:summary="@string/preference_energy_saving_sending_interval_summary"/>
<EditTextPreference
android:key="@string/preference_energy_saving_sending_interval_message_key"
android:selectable="false"/>
</PreferenceCategory>
<!-- Connection -->
@@ -68,7 +68,7 @@ public class TrackingService extends Service implements GoogleApiClient.Connecti
private int NOTIFICATION_ID = R.string.tracker_started;
public final static int UPDATE_INTERVAL_IN_MILLIS_DEFAULT = 1000;
private final static int UPDATE_INTERVAL_IN_MILLIS_POWERSAVE_MODE = 30000;
private final static int UPDATE_INTERVAL_IN_MILLIS_POWERSAVE_MODE = 20000;
private final static float BATTERY_POWER_SAVE_THRESHOLD = 0.2f;
private boolean initialLocation;
@@ -163,6 +163,7 @@ public class TrackingService extends Service implements GoogleApiClient.Connecti
prefs.setTrackerIsTracking(true);
prefs.setTrackerIsTrackingCheckinDigest(checkinDigest);
prefs.setEnergySavingOverride(false);
}
private void stopTracking() {
@@ -348,17 +349,18 @@ public class TrackingService extends Service implements GoogleApiClient.Connecti
}
/**
* Update whether message sending service should retry every 3 seconds or every 30.
* Update the interval in which message sending service should retry - possibly enable power saving
*/
private void updateResendIntervalSetting() {
float batteryPct = getBatteryPercentage();
boolean batteryIsCharging = prefs.getBatteryIsCharging();
int updateInterval = UPDATE_INTERVAL_IN_MILLIS_DEFAULT;
if (prefs.getEnergySavingEnabledByUser() || (batteryPct < BATTERY_POWER_SAVE_THRESHOLD && !batteryIsCharging)) {
int updateInterval = prefs.getMessageSendingIntervalInMillis();
if (batteryPct < BATTERY_POWER_SAVE_THRESHOLD && !batteryIsCharging && !prefs.getEnergySavingOverride()) {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in power saving mode");
}
updateInterval = UPDATE_INTERVAL_IN_MILLIS_POWERSAVE_MODE;
prefs.setEnergySavingEnabledAutomatically(true);
} else {
if (BuildConfig.DEBUG) {
ExLog.i(this, "POWER-LEVELS", "in default power mode");
@@ -7,11 +7,11 @@ import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivity;
@@ -32,6 +32,8 @@ public class TrackingFragment extends BaseFragment {
private AppPreferences prefs;
private long lastGPSQualityUpdate;
private Toast gpsToast;
private boolean gpsFound = true;
private String TAG = TrackingFragment.class.getName();
@@ -69,6 +71,9 @@ public class TrackingFragment extends BaseFragment {
qualityIndicator.setSignalQuality(savedInstanceState.getInt(SIS_GPS_QUALITY));
accuracyText.setText(savedInstanceState.getString(SIS_GPS_ACCURACY));
unsentFixesText.setText(savedInstanceState.getString(SIS_GPS_UNSENT_FIXES));
} else {
//initially set quality to "No GPS" on start tracking
updateTrackingStatus(GPSQuality.noSignal);
}
}
@@ -115,9 +120,22 @@ public class TrackingFragment extends BaseFragment {
if (quality == GPSQuality.noSignal) {
textView.setText(getString(R.string.tracking_status_no_gps_signal));
textView.setTextColor(getResources().getColor(R.color.sap_red));
//either GPS has been found before or tracking just started (but is not available now) --> show noGPS Toast
if (gpsFound || gpsToast == null) {
gpsToast = Toast.makeText(getActivity(),getString(R.string.tracking_status_no_gps_signal), Toast.LENGTH_LONG);
gpsToast.show();
gpsFound = false;
}
} else {
textView.setText(getString(R.string.tracking_status_tracking));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
gpsToast.cancel();
//GPS was lost before but is available again now --> show gpsFound toast
if (!gpsFound) {
gpsToast = Toast.makeText(getActivity(),getString(R.string.tracking_status_gps_found), Toast.LENGTH_SHORT);
gpsToast.show();
gpsFound = true;
}
}
}
}
@@ -134,9 +152,9 @@ public class TrackingFragment extends BaseFragment {
public void run() {
TextView textView = (TextView) getActivity().findViewById(R.id.mode);
if (apiConnectivity == APIConnectivity.transmissionSuccess) {
if (prefs.getEnergySavingEnabledByUser()) {
if (prefs.getEnergySavingEnabledAutomatically() && !prefs.getEnergySavingOverride()) {
textView.setText(getString(R.string.tracking_mode_battery_saving));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
textView.setTextColor(getResources().getColor(R.color.sap_red));
} else {
textView.setText(getString(R.string.tracking_mode_live));
textView.setTextColor(getResources().getColor(R.color.fiori_text_color));
@@ -183,7 +201,7 @@ public class TrackingFragment extends BaseFragment {
} else {
locationProviders = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
return locationProviders.contains("gps");
}
} else {
return false;
@@ -1,7 +1,9 @@
package com.sap.sailing.android.tracking.app.ui.fragments.preference;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.SwitchPreference;
import com.sap.sailing.android.shared.ui.fragments.preference.BasePreferenceFragment;
@@ -9,8 +11,7 @@ import com.sap.sailing.android.shared.util.UniqueDeviceUuid;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
public class GeneralPreferenceFragment extends BasePreferenceFragment {
public class GeneralPreferenceFragment extends BasePreferenceFragment implements OnPreferenceChangeListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -24,23 +25,83 @@ public class GeneralPreferenceFragment extends BasePreferenceFragment {
if (currentDeviceId != null && currentDeviceId.length() > 0) {
deviceIdPreference.setSummary(currentDeviceId);
} else {
// this would, in most caes, be set elsewhere, but since we don't want to show an empty summary,
// this would, in most cases, be set elsewhere, but since we don't want to show an empty summary,
// we can just set it here. (UniqueDeviceUuid.getUniqueId sets it in prefs.)
String newDeviceId = UniqueDeviceUuid.getUniqueId(getActivity());
deviceIdPreference.setSummary(newDeviceId);
}
ListPreference prefSendingInterval = findPreference(R.string.preference_energy_saving_sending_interval_key);
prefSendingInterval.setDefaultValue(R.string.preference_energy_saving_sending_interval_default);
prefSendingInterval.setOnPreferenceChangeListener(this);
updateEnergySavingMessage(null);
}
@Override
public void onPause() {
super.onPause();
SwitchPreference prefEnergy = findPreference(R.string.preference_energy_saving_enabled_key);
//SwitchPreference prefEnergy = findPreference(R.string.preference_energy_saving_enabled_key);
SwitchPreference prefDeclination = findPreference(R.string.preference_heading_with_declination_subtracted_key);
AppPreferences prefs = new AppPreferences(getActivity());
prefs.setEnergySavingEnabledByUser(prefEnergy.isChecked());
//prefs.setEnergySavingEnabledByUser(prefEnergy.isChecked());
prefs.setDisplayHeadingWithSubtractedDeclination(prefDeclination.isChecked());
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
AppPreferences prefs = new AppPreferences(getActivity());
//GPS message sending interval settings
if(preference.getKey().equals(getString(R.string.preference_energy_saving_sending_interval_key))) {
int msgSndInt = Integer.parseInt((String)newValue);
switch (msgSndInt){
case 0:
//send every second
prefs.setMessageResendIntervalInMillis(1000);
break;
default:
prefs.setMessageResendIntervalInMillis(msgSndInt * 1000);
break;
}
if(prefs.getEnergySavingEnabledAutomatically())
prefs.setEnergySavingOverride(true);
updateEnergySavingMessage(newValue.toString());
}
return true;
}
private void updateEnergySavingMessage(String value){
Preference msgSndIntMessage = findPreference(getString(R.string.preference_energy_saving_sending_interval_message_key));
ListPreference msgSndIntPreference = (ListPreference) findPreference(getString(R.string.preference_energy_saving_sending_interval_key));
//when value == null, (meaning the settings screen just launched) - current entry will be selected
//otherwise, we need to get the index of the newly selected entry as the one resulting from getEntry() will be the previous one
String entry;
if (value == null){
entry = msgSndIntPreference.getEntry().toString();
} else {
int indexOfEntry = msgSndIntPreference.findIndexOfValue(value);
entry = msgSndIntPreference.getEntries()[indexOfEntry].toString();
}
//possibly fill in the word "every"
String fillEvery = (entry.equals(msgSndIntPreference.getEntries()[0])) ? "" : " " + getString(R.string.preference_energy_saving_sending_interval_message_every);
//set Text
AppPreferences prefs = new AppPreferences(getActivity());
if(prefs.getEnergySavingEnabledAutomatically() && !prefs.getEnergySavingOverride()){
msgSndIntMessage.setSummary(getString(R.string.preference_energy_saving_enabled_automatically));
} else {
msgSndIntMessage.setSummary(String.format(
getString(R.string.preference_energy_saving_sending_interval_message),
fillEvery,
entry)
);
}
}
}
@@ -112,15 +112,24 @@ public class AppPreferences extends BaseAppPreferences {
preferences.edit().putString(context.getString(R.string.preference_competitor_key), id).commit();
}
public void setEnergySavingEnabledByUser(boolean newValue) {
public void setEnergySavingEnabledAutomatically(boolean newValue) {
preferences.edit().putBoolean(context.getString(R.string.preference_energy_saving_enabled_key), newValue)
.commit();
}
public boolean getEnergySavingEnabledByUser() {
public boolean getEnergySavingEnabledAutomatically() {
return preferences.getBoolean(context.getString(R.string.preference_energy_saving_enabled_key), false);
}
public void setEnergySavingOverride(boolean newValue) {
preferences.edit().putBoolean(context.getString(R.string.preference_energy_saving_override_key), newValue)
.commit();
}
public boolean getEnergySavingOverride() {
return preferences.getBoolean(context.getString(R.string.preference_energy_saving_override_key), false);
}
public void setDisplayHeadingWithSubtractedDeclination(boolean newValue) {
preferences.edit()
.putBoolean(context.getString(R.string.preference_heading_with_declination_subtracted_key), newValue)
@@ -186,4 +195,5 @@ public class AppPreferences extends BaseAppPreferences {
public void removeFailedUpload(String key) {
pref.edit().remove(key).commit();
}
}
@@ -9,6 +9,7 @@
<string name="reset_position">Position zurücksetzen</string>
<string name="getting_marks">Empfangen der Markierungen&#8230;</string>
<string name="set">Gesetzt</string>
<string name="unknown">Unbekannt</string>
<string name="not_set">Nicht gesetzt</string>
<string name="mark_list_accuracy">(Genauigkeit ca. %1$sm)</string>
<string name="accuracy">GPS Genauigkeit</string>
@@ -12,6 +12,7 @@
<string name="reset_position">Reset Position</string>
<string name="getting_marks">Getting race marks&#8230;</string>
<string name="set">set</string>
<string name="unknown">n/a</string>
<string name="not_set">not set</string>
<string name="mark_list_accuracy">(Accurary ca. %1$sm)</string>
<string name="buoy_detail_accuracy_ca" translatable="false">ca. %1$.02fm</string>
@@ -33,8 +33,16 @@ public class MarkAdapter extends ResourceCursorAdapter {
} else {
final DecimalFormat df = new DecimalFormat("#.##");
final double accuracy = markPings.get(0).getAccuracy();
final String accuracyString = String.format(context.getString(R.string.mark_list_accuracy), df.format(accuracy));
setText = context.getString(R.string.set) + " " + accuracyString;
String accuracyString= "";
//accuracy-values that are stored as -1 will be displayed as "n/a"
if(accuracy == MarkPingInfo.NOT_SET_BY_USER){
accuracyString = context.getString(R.string.unknown);
}
else {
accuracyString = context.getString(R.string.set) + " " +
String.format(context.getString(R.string.mark_list_accuracy), df.format(accuracy));
}
setText = accuracyString;
}
markName.setText(name);
markSet.setText(setText);
@@ -55,7 +55,6 @@ public class BuoyFragment extends BaseFragment
private static final String TAG = BuoyFragment.class.getName();
private static final int GPS_MIN_TIME = 1000;
private static final String N_A = "n/a";
private static final int REQUEST_CHECK_SETTINGS = 1 << 16;
private TextView accuracyTextView;
@@ -230,8 +229,8 @@ public class BuoyFragment extends BaseFragment
}
private void setUpTextUI(Location location) {
String accuracyText = N_A;
String distanceText = N_A;
String accuracyText = this.getString(R.string.unknown);
String distanceText = this.getString(R.string.unknown);
if (location != null) {
accuracyText = getString(R.string.buoy_detail_accuracy_ca, location.getAccuracy());
MarkPingInfo markPing = positioningActivity.getMarkPing();
@@ -189,10 +189,19 @@ public class CheckinManager {
org.json.simple.JSONObject simplePosition;
simplePosition = JsonHelper.convertToSimple(positionJson);
GPSFix gpsFix = deserializer.deserialize(simplePosition);
MarkPingInfo ping = new MarkPingInfo(mark.getId(), gpsFix, positionJson.getDouble(FlatGPSFixJsonSerializer.FIELD_ACCURACY));
//accepts JSON messages without accuracy and with, without will be displayed as "n/a"
MarkPingInfo ping;
if (positionJson.has(FlatGPSFixJsonSerializer.FIELD_ACCURACY)){
ping = new MarkPingInfo(mark.getId(), gpsFix, positionJson.getDouble(FlatGPSFixJsonSerializer.FIELD_ACCURACY));
}
else {
ping = new MarkPingInfo(mark.getId(), gpsFix, MarkPingInfo.NOT_SET_BY_USER);
}
if (ping != null){
pings.add(ping);
}
}
}
marks.add(mark);
}
urlData.marks = marks;
@@ -9,6 +9,8 @@ public class MarkPingInfo {
private final GPSFix gpsFix;
private final double accuracy;
public static final int NOT_SET_BY_USER = -1; // this is used for accuracies that are displayed as "n/a"
public MarkPingInfo(Serializable markId, GPSFix gpsFix, double accuracy) {
super();
this.markId = markId;
+6 -2
View File
@@ -6,9 +6,13 @@ This article can be used to store information on the data consumption that can b
Energy consumption of the tracking itself is - without any optimization and compression of the messages at the time of writing this - quite low already. Ten hours of tracking mean less than five MB of data volume for the messages themselve, if you choose to send them immediately. If sent in intervalls you can minimize the size of the messages further, since you do not have to sent the devices UUID with every fix. Sending every second message together with the previous one will theoretically equal to around three to four MB of data volume needed for ten hours of tracking.
It is technically possible to compress the sent JSON messages with GZIP. Even if every message is sent individually you can achieve a compression ratio of ~103% which on it's own is not a lot, but at least it does not make it worse. Where it would really come in handy is when several messages are sent together. At a 5 second sending intervall, you can already achieve compression ratios of 300% and more. The challenge with sending GZIP-compressed JSON messages to the server is that this has to be supported by the webserver. Normally only messages sent from the server to the client can contain compressed content. There are ways to enable Apaches to receive compressed content, this definitely could use some testing.
It is technically possible to compress the sent JSON messages with GZIP. Even if every message is sent individually you can achieve a compression ratio of ~103% which on it's own is not a lot, but at least it does not make it worse. Where it would really come in handy is when several messages are sent together. At a 5 second sending intervall, you can already achieve compression ratios of 300% and more. The challenge with sending GZIP-compressed JSON messages to the server is that this has to be supported by the webserver. Normally only messages sent from the server to the client can contain compressed content. There are ways to enable Apaches to receive compressed content but that probably is a lot of work.
Since the app not only produces traffic when tracking, but also when checking in to a event or reopening that, when loading and sending team photos etc. energy consumption is significantly higher than the previously mentioned values. Since we cannot determine how often users will open and close the app, open the regattas, refresh photos etc. we cannot give good estimations on that even if we knew how much data volume every single one of these actions costs.
A better idea to cut down on data consumption of the fixes messages in the InSight app is to assemble byte messages directly instead of taking JSON messages and encoding them with UTF-8. You can simply assemble a DataOutputStream / ByteArrayOutputStream with the UUID as 2 64-bit longs at the beginning and then append the values of all the parameters in a certain order with certain data types of certain lengths. On the server-side you can then read that stream of data, extract the first 16 bytes as UUID and then split the rest of the data into the fixes again.
On [Bug 3933](https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=3933) you can already find an implementation for assembling such a byte message. We already evaluated that you can save more than two thirds of the message length by doing this.
This only has to be implemented further down to the point where the data is sent and on the server side, possibly with a parameter in the URL to enable the servers to accept both JSON and direct byte messages, an implementation to split them apart again.
Since the app not only produces traffic when tracking, but also when checking in to a event or reopening that, when loading and sending team photos etc. energy consumption is significantly higher than the previously mentioned values. Since we cannot determine how often users will open and close the app, open the regattas, refresh photos etc. we cannot give good estimations for real world use cases even if we knew how much data volume every single one of these actions costs.
What we can say is, that testing the app extensively over a time of four weeks with booking into events probably more than 100 times and tracking way more than 30 hours caused around 50 to 100 MB of traffic on the testing phones.
Under normal conditions the traffic for one event should stay well below the former of these numbers.