Browse code

new UI with more data and cleaning + build

louis.jonget authored on10/11/2022 14:48:43
Showing4 changed files
1 1
Binary files a/build/bike_companion.pbw and b/build/bike_companion.pbw differ
... ...
@@ -21,6 +21,9 @@
21 21
       "watchface": false
22 22
     },
23 23
     "messageKeys": [
24
+      "distance",
25
+      "avg_speed",
26
+      "duration",
24 27
       "latitude",
25 28
       "longitude",
26 29
       "accuracy",
... ...
@@ -1,11 +1,16 @@
1 1
 #include <pebble.h>
2 2
 
3 3
 static Window *s_window;
4
-static TextLayer *s_speed_text_layer;
5
-static TextLayer *s_other_text_layer;
4
+static StatusBarLayer *s_status_text_layer;
5
+static TextLayer *s_upperleft_text_layer;
6
+static TextLayer *s_upperright_text_layer;
7
+static TextLayer *s_main_text_layer;
8
+static TextLayer *s_lowerleft_text_layer;
9
+static TextLayer *s_lowerright_text_layer;
10
+static TextLayer *s_lower_text_layer;
6 11
 
7 12
 // Largest expected inbox and outbox message sizes
8
-const uint32_t inbox_size = 64;
13
+const uint32_t inbox_size = 150;
9 14
 const uint32_t outbox_size = 64;
10 15
 
11 16
 static uint32_t size ;
... ...
@@ -16,13 +21,16 @@ static char s_status[4];
16 21
 //restricting long and lat to 13 char : 0-1 for the sign, 1-3 for integer, 1 for the point, 7-10 for decimal, 1 for \0
17 22
 static char s_longitude[13];
18 23
 static char s_latitude[13];
24
+static char s_distance[6];
19 25
 
20 26
 static char s_accuracy[5];
21 27
 static char s_altitude[6];
22 28
 static char s_altitude_accuracy[4];
23 29
 static char s_timestamp[14];
30
+static char s_duration[9];
24 31
 static char s_speed[6];
25 32
 static char s_max_speed[5];
33
+static char s_avg_speed[4];
26 34
 
27 35
 static char s_msg[50];
28 36
 
... ...
@@ -32,12 +40,15 @@ typedef enum {
32 40
   status,
33 41
   latitude,
34 42
   longitude,
43
+  distance,
35 44
   altitude,
36 45
   accuracy,
37 46
   altitude_accuracy,
38 47
   speed,
39 48
   max_speed,
40
-  timestamp
49
+  avg_speed,
50
+  timestamp,
51
+  duration
41 52
 } AppKey;
42 53
 
43 54
 
... ...
@@ -65,25 +76,27 @@ static void send_message(char * msg){
65 76
 
66 77
 
67 78
 static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) {
68
-  text_layer_set_text(s_speed_text_layer, "");
69
-  text_layer_set_text(s_other_text_layer, "sending get geoloc");
70
-  send_message("get");
79
+  text_layer_set_text(s_main_text_layer, "");
80
+  text_layer_set_text(s_lower_text_layer, "sending get geoloc");
81
+  send_message("startstop");
71 82
 }
72 83
 
73 84
 static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) {
74
-  text_layer_set_text(s_other_text_layer, "Tracking stopped, sending...");
75
-  send_message("exit");
85
+  text_layer_set_text(s_lower_text_layer, "Tracking stopped, sending track...");
86
+  send_message("send");
76 87
 }
77 88
 
78 89
 static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) {
79
-  text_layer_set_text(s_other_text_layer, "Tracking stopped, sending...");
80
-  send_message("exit");
90
+  //text_layer_set_text(s_lower_text_layer, "Tracking stopped, sending track...");
91
+  //send_message("exit");
81 92
 }
82 93
 
94
+/*
83 95
 static void prv_back_click_handler(ClickRecognizerRef recognizer, void *context) {
84 96
   //text_layer_set_text(s_other_text_layer, "back");
85 97
   //send_message("exit");
86 98
 }
99
+*/
87 100
 
88 101
 static void prv_click_config_provider(void *context) {
89 102
   window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler);
... ...
@@ -96,33 +109,82 @@ static void prv_window_load(Window *window) {
96 109
   Layer *window_layer = window_get_root_layer(window);
97 110
   GRect bounds = layer_get_bounds(window_layer);
98 111
 
99
-  s_speed_text_layer = text_layer_create(GRect(0, 0, bounds.size.w, bounds.size.h/2));
100
-
101
-  text_layer_set_background_color(s_speed_text_layer, GColorBlack);
102
-  text_layer_set_text_color(s_speed_text_layer, GColorWhite);
103
-  text_layer_set_font(s_speed_text_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
104
-  text_layer_set_text_alignment(s_speed_text_layer, GTextAlignmentCenter);
105
-  layer_add_child(window_layer, text_layer_get_layer(s_speed_text_layer));
106
-
107
-
108
-  s_other_text_layer = text_layer_create(GRect(0, bounds.size.h/2,bounds.size.w, bounds.size.h/2));
112
+  // creation of all text layers
113
+  s_status_text_layer = status_bar_layer_create();
114
+  s_upperleft_text_layer = text_layer_create(GRect(0, 18, bounds.size.w/2, 34));
115
+  s_upperright_text_layer = text_layer_create(GRect(bounds.size.w/2, 18, bounds.size.w/2, 34));
116
+  s_main_text_layer = text_layer_create(GRect(0, 52, bounds.size.w, 52));
117
+  s_lowerleft_text_layer = text_layer_create(GRect(0, 104, bounds.size.w/2, 34));
118
+  s_lowerright_text_layer = text_layer_create(GRect(bounds.size.w/2, 104, bounds.size.w/2, 34));
119
+  s_lower_text_layer = text_layer_create(GRect(0, 138, bounds.size.w, 30));
120
+
121
+  // settings of status layer
122
+  status_bar_layer_set_colors(s_status_text_layer, GColorBlack, GColorWhite);
123
+  status_bar_layer_set_separator_mode(s_status_text_layer, StatusBarLayerSeparatorModeDotted);
124
+  layer_set_frame(status_bar_layer_get_layer(s_status_text_layer), GRect(0, 0, bounds.size.w, 18));
125
+  layer_add_child(window_layer, status_bar_layer_get_layer(s_status_text_layer));
126
+  /*
127
+  may need to add other layers to disply BT and GPS in status bar ?
128
+  */
129
+
130
+  // settings of upperleft layer
131
+  text_layer_set_background_color(s_upperleft_text_layer, GColorWhite);
132
+  text_layer_set_text_color(s_upperleft_text_layer, GColorBlack);
133
+  text_layer_set_font(s_upperleft_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
134
+  text_layer_set_text_alignment(s_upperleft_text_layer, GTextAlignmentCenter);
135
+  layer_add_child(window_layer, text_layer_get_layer(s_upperleft_text_layer));
136
+
137
+  // settings of upperright layer
138
+  text_layer_set_background_color(s_upperright_text_layer, GColorWhite);
139
+  text_layer_set_text_color(s_upperright_text_layer, GColorBlack);
140
+  text_layer_set_font(s_upperright_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
141
+  text_layer_set_text_alignment(s_upperright_text_layer, GTextAlignmentCenter);
142
+  layer_add_child(window_layer, text_layer_get_layer(s_upperright_text_layer));
143
+
144
+  // settings of main layer
145
+  text_layer_set_background_color(s_main_text_layer, GColorBlack);
146
+  text_layer_set_text_color(s_main_text_layer, GColorWhite);
147
+  text_layer_set_font(s_main_text_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
148
+  text_layer_set_text_alignment(s_main_text_layer, GTextAlignmentCenter);
149
+  layer_add_child(window_layer, text_layer_get_layer(s_main_text_layer));
150
+
151
+  // settings of lowerleft layer
152
+  text_layer_set_background_color(s_lowerleft_text_layer, GColorWhite);
153
+  text_layer_set_text_color(s_lowerleft_text_layer, GColorBlack);
154
+  text_layer_set_font(s_lowerleft_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
155
+  text_layer_set_text_alignment(s_lowerleft_text_layer, GTextAlignmentCenter);
156
+  layer_add_child(window_layer, text_layer_get_layer(s_lowerleft_text_layer));
157
+
158
+  // settings of lowerright layer
159
+  text_layer_set_background_color(s_lowerright_text_layer, GColorWhite);
160
+  text_layer_set_text_color(s_lowerright_text_layer, GColorBlack);
161
+  text_layer_set_font(s_lowerright_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
162
+  text_layer_set_text_alignment(s_lowerright_text_layer, GTextAlignmentCenter);
163
+  layer_add_child(window_layer, text_layer_get_layer(s_lowerright_text_layer));
164
+
165
+  // settings of lower layer
166
+  text_layer_set_background_color(s_lower_text_layer, GColorBlack);
167
+  text_layer_set_text_color(s_lower_text_layer, GColorWhite);
168
+  text_layer_set_font(s_lower_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
169
+  text_layer_set_text_alignment(s_lower_text_layer, GTextAlignmentCenter);
170
+  layer_add_child(window_layer, text_layer_get_layer(s_lower_text_layer));
109 171
 
110
-  text_layer_set_background_color(s_other_text_layer, GColorClear);
111
-  text_layer_set_text_color(s_other_text_layer, GColorBlack);
112
-  text_layer_set_font(s_other_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
113
-  text_layer_set_text_alignment(s_other_text_layer, GTextAlignmentCenter);
114
-  layer_add_child(window_layer, text_layer_get_layer(s_other_text_layer));
115 172
 }
116 173
 
117 174
 static void prv_window_unload(Window *window) {
118
-  text_layer_destroy(s_speed_text_layer);
175
+  text_layer_destroy(s_main_text_layer);
119 176
 }
120 177
 
121 178
 void comm_is_ready() {
122 179
 
123 180
   // set the text
124
-  text_layer_set_text(s_speed_text_layer, "Press Select");
125
-  text_layer_set_text(s_other_text_layer, "Welcome to Bike Companion ! JS is ready");
181
+  //text_layer_set_text(s_status_text_layer, "0......................................");
182
+  text_layer_set_text(s_upperleft_text_layer, "-");
183
+  text_layer_set_text(s_upperleft_text_layer, "-");
184
+  text_layer_set_text(s_main_text_layer, "Ready");
185
+  text_layer_set_text(s_lowerleft_text_layer, "-");
186
+  text_layer_set_text(s_lowerright_text_layer, "-");
187
+  text_layer_set_text(s_lower_text_layer, "--:--:--");
126 188
 
127 189
 }
128 190
 
... ...
@@ -170,6 +232,16 @@ static void inbox_received_callback(DictionaryIterator *iter, void *context) {
170 232
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "not longitude message... ");
171 233
   }
172 234
 
235
+  // Read distance returns
236
+  Tuple *distance_tuple = dict_find(iter, MESSAGE_KEY_distance);
237
+
238
+  if(distance_tuple) {
239
+    memset(s_distance,'\0',sizeof(s_distance));
240
+    strncpy(s_distance, distance_tuple->value->cstring, 6);
241
+  }else{
242
+    //APP_LOG(APP_LOG_LEVEL_DEBUG, "not distance message... ");
243
+  }
244
+
173 245
   // Read accuracy returns
174 246
   Tuple *accuracy_tuple = dict_find(iter, MESSAGE_KEY_accuracy);
175 247
 
... ...
@@ -226,6 +298,16 @@ static void inbox_received_callback(DictionaryIterator *iter, void *context) {
226 298
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "not timestamp message... ");
227 299
   }
228 300
 
301
+  // Read duration returns
302
+  Tuple *duration_tuple = dict_find(iter, MESSAGE_KEY_duration);
303
+
304
+  if(duration_tuple) {
305
+    memset(s_duration,'\0',sizeof(s_duration));
306
+    strncpy(s_duration, duration_tuple->value->cstring, 9);
307
+    //APP_LOG(APP_LOG_LEVEL_DEBUG, "duration message received : %s",s_duration);
308
+  }else{
309
+    //APP_LOG(APP_LOG_LEVEL_DEBUG, "not duration message... ");
310
+  }
229 311
 
230 312
   // Read speed returns
231 313
   Tuple *speed_tuple = dict_find(iter, MESSAGE_KEY_speed);
... ...
@@ -255,6 +337,15 @@ static void inbox_received_callback(DictionaryIterator *iter, void *context) {
255 337
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "not max_speed message... ");
256 338
   }
257 339
 
340
+  // Read avg_speed returns
341
+  Tuple *avg_speed_tuple = dict_find(iter, MESSAGE_KEY_avg_speed);
342
+
343
+  if(avg_speed_tuple) {
344
+    memset(s_avg_speed,'\0',sizeof(s_avg_speed));
345
+    strncpy(s_avg_speed, avg_speed_tuple->value->cstring, 4);
346
+  }else{
347
+    //APP_LOG(APP_LOG_LEVEL_DEBUG, "not avg_speed message... ");
348
+  }
258 349
 
259 350
   // Read status returns
260 351
   Tuple *status_tuple = dict_find(iter, MESSAGE_KEY_status);
... ...
@@ -266,25 +357,29 @@ static void inbox_received_callback(DictionaryIterator *iter, void *context) {
266 357
 
267 358
     // DEBUG concatenate all data received
268 359
     /*
269
-    memset(s_msg,'\0',sizeof(s_msg));
270
-    strncpy(s_msg, "Acc. ",5);
271
-    strcat(s_msg, s_accuracy);
272
-    strcat(s_msg, "    Alt. ");
273
-    strcat(s_msg, s_altitude);
274
-    strcat(s_msg, "\n Max. ");
275
-    strcat(s_msg, s_max_speed);
276
-    strcat(s_msg, "\n Lat. ");
277
-    strcat(s_msg, s_latitude);
278
-    strcat(s_msg, "\n Long. ");
279
-    strcat(s_msg, s_longitude);
280
-    strcat(s_msg, "\n Time. ");
281
-    strcat(s_msg, s_timestamp);
360
+      memset(s_msg,'\0',sizeof(s_msg));
361
+      strncpy(s_msg, "Acc. ",5);
362
+      strcat(s_msg, s_accuracy);
363
+      strcat(s_msg, "    Alt. ");
364
+      strcat(s_msg, s_altitude);
365
+      strcat(s_msg, "\n Max. ");
366
+      strcat(s_msg, s_max_speed);
367
+      strcat(s_msg, "\n Lat. ");
368
+      strcat(s_msg, s_latitude);
369
+      strcat(s_msg, "\n Long. ");
370
+      strcat(s_msg, s_longitude);
371
+      strcat(s_msg, "\n Time. ");
372
+      strcat(s_msg, s_timestamp);
282 373
     */
283 374
 
284 375
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "to display : %s ",s_msg);
285 376
 
286
-    text_layer_set_text(s_speed_text_layer, s_speed);
287
-    text_layer_set_text(s_other_text_layer, s_msg);
377
+    text_layer_set_text(s_upperleft_text_layer, s_altitude);
378
+    text_layer_set_text(s_upperright_text_layer, s_distance);
379
+    text_layer_set_text(s_main_text_layer, s_speed);
380
+    text_layer_set_text(s_lowerleft_text_layer, s_avg_speed);
381
+    text_layer_set_text(s_lowerright_text_layer, s_max_speed);
382
+    text_layer_set_text(s_lower_text_layer, s_duration);
288 383
   }else{
289 384
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "not status message... ");
290 385
   }
... ...
@@ -4,138 +4,205 @@ var clay = new Clay(clayConfig, null, { autoHandleEvents: false });
4 4
 var messageKeys = require('message_keys');
5 5
 
6 6
 var message;
7
-var gpx_to_strava
8
-var gpx_to_web
9 7
 var locate_me
10 8
 
11
-var locationInterval;
12
-var instantLocationInterval;
9
+var firstlocationInterval = false;
10
+var locationInterval = false;
11
+var instantLocationInterval = false;
13 12
 
14 13
 // TODO to remove for security
15 14
 var client_id = "94880";
16 15
 var client_secret = "08dc170f0fe38f39dd327bea82a28db4400e6f00";
17 16
 
18 17
 var firstlocationOptions = {
19
-    'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
20
-    'timeout': 60000, //60s timeout to get a first good signal
21
-    'maximumAge': 0 // no cache
18
+  'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
19
+  'timeout': 60000, //60s timeout to get a first good signal
20
+  'maximumAge': 0 // no cache
22 21
 };
23 22
 var locationOptions = {
24
-    'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
25
-    'timeout': 5000, //5s timeout to get a good signal
26
-    'maximumAge': 0 // no cache
23
+  'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
24
+  'timeout': 5000, //5s timeout to get a good signal
25
+  'maximumAge': 0 // no cache
27 26
 };
28 27
 
29
-Pebble.addEventListener('showConfiguration', function(e) {
30
-    clay.config = clayConfig;
31
-    console.log("Clay config is showing...")
32
-    Pebble.openURL(clay.generateUrl());
28
+Pebble.addEventListener('showConfiguration', function (e) {
29
+  clay.config = clayConfig;
30
+  console.log("Clay config is showing...")
31
+  Pebble.openURL(clay.generateUrl());
33 32
 });
34 33
 
35
-Pebble.addEventListener('webviewclosed', function(t) {
36
-    if (!t || t.response) {
37
-        console.log("Clay config is submitted : " + t.response)
38
-        try {
39
-            if (data = JSON.parse(t.response), data.code && data.scope == "read,activity:write") {
40
-                if (data.state == "bike_companion" && data.scope == "read,activity:write") {
41
-                    getTokens(data.code);
42
-                } else {
43
-                    console.log("Error on response returned : scope is " + grantcode.scope + " and state is " + grantcode.state);
44
-                }
45
-            } else {
46
-                clay.getSettings(t.response);
47
-                console.log("Clay settings in Localstorage looks like " + localStorage.getItem("clay-settings"));
48
-            }
49
-        } catch (t) {
50
-            console.log("Oauth parsing error, continue on saving clay settings");
51
-            clay.getSettings(t.response);
52
-            var claysettings = JSON.parse(localStorage.getItem('clay-settings'))
53
-            claysettings.strava_enabled = false;
54
-            localStorage.setItem("clay-settings", JSON.stringify("claysettings"));
55
-            console.log("Clay settings in Localstorage looks like " + localStorage.getItem("clay-settings"));
34
+Pebble.addEventListener('webviewclosed', function (t) {
35
+  if (!t || t.response) {
36
+    console.log("Clay config is submitted : " + t.response)
37
+    try {
38
+      if (data = JSON.parse(t.response), data.code && data.scope == "read,activity:write") {
39
+        if (data.state == "bike_companion" && data.scope == "read,activity:write") {
40
+          getTokens(data.code);
41
+        } else {
42
+          console.log("Error on response returned : scope is " + grantcode.scope + " and state is " + grantcode.state);
56 43
         }
57
-
44
+      } else {
45
+        clay.getSettings(t.response);
46
+        console.log("Clay settings in Localstorage looks like " + localStorage.getItem("clay-settings"));
47
+      }
48
+    } catch (t) {
49
+      console.log("Oauth parsing error, continue on saving clay settings");
50
+      clay.getSettings(t.response);
51
+      var claysettings = JSON.parse(localStorage.getItem('clay-settings'))
52
+      claysettings.strava_enabled = false;
53
+      localStorage.setItem("clay-settings", JSON.stringify("claysettings"));
54
+      console.log("Clay settings in Localstorage looks like " + localStorage.getItem("clay-settings"));
58 55
     }
56
+
57
+  }
59 58
 });
60 59
 
60
+// Calculate the distance from 2 geoloc in degrees.
61
+// IMPORTANT : this is a calculation from 2D projection, altitude is not involved
62
+//
63
+function distance_on_geoid(lat1, lon1, lat2, lon2) {
64
+  // Convert degrees to radians
65
+  lat1 = lat1 * Math.PI / 180.0;
66
+  lon1 = lon1 * Math.PI / 180.0;
67
+  lat2 = lat2 * Math.PI / 180.0;
68
+  lon2 = lon2 * Math.PI / 180.0;
69
+  // radius of earth in metres
70
+  r = 6378100;
71
+  // P
72
+  rho1 = r * Math.cos(lat1);
73
+  z1 = r * Math.sin(lat1);
74
+  x1 = rho1 * Math.cos(lon1);
75
+  y1 = rho1 * Math.sin(lon1);
76
+  // Q
77
+  rho2 = r * Math.cos(lat2);
78
+  z2 = r * Math.sin(lat2);
79
+  x2 = rho2 * Math.cos(lon2);
80
+  y2 = rho2 * Math.sin(lon2);
81
+  // Dot product
82
+  dot = (x1 * x2 + y1 * y2 + z1 * z2);
83
+  cos_theta = dot / (r * r);
84
+  theta = Math.acos(cos_theta);
85
+  // Distance in Metres
86
+  return r * theta;
87
+}
88
+
89
+// Adding leading characters to string for nice displays
90
+//
91
+function padStart(string, max_length, padding) {
92
+  if (string.length > max_length) {
93
+    return string;
94
+  } else {
95
+    var new_str = string;
96
+    for (index = string.length; index < max_length; index++) {
97
+      new_str = padding + new_str;
98
+    }
99
+    return new_str;
100
+  }
101
+}
102
+
61 103
 
62 104
 // Store location in Pebble app local storage
63 105
 //
64
-function storeLocation(position) {
65
-    var latitude = position.coords.latitude;
66
-    var longitude = position.coords.longitude;
67
-    var timestamp = position.timestamp;
68
-    localStorage.setItem("latitude", latitude);
69
-    localStorage.setItem("longitude", longitude);
70
-    localStorage.setItem("timestamp", timestamp);
71
-    // console.log("Stored location " + position.coords.latitude + ',' + position.coords.longitude);
106
+function storeLocation(position, first) {
107
+  var latitude = position.coords.latitude;
108
+  var longitude = position.coords.longitude;
109
+  var timestamp = position.timestamp;
110
+  if (first == true) {
111
+    localStorage.setItem("firstlatitude", latitude);
112
+    localStorage.setItem("firstlongitude", longitude);
113
+    localStorage.setItem("firsttimestamp", Date.now());
114
+    //console.log("-- First timestamp: " + localStorage.getItem("firsttimestamp"));
115
+    localStorage.setItem("totalcoordinates", 0)
116
+  }
117
+
118
+  localStorage.setItem("lastlatitude", latitude);
119
+  localStorage.setItem("lastlongitude", longitude);
120
+  localStorage.setItem("lasttimestamp", timestamp);
121
+  localStorage.setItem("totalcoordinates", parseInt(localStorage.getItem("totalcoordinates")) + 1)
122
+  // console.log("Stored location " + position.coords.latitude + ',' + position.coords.longitude);
72 123
 }
73 124
 
74 125
 // Get location from Pebble app local storage
75 126
 //
76
-function getLocation() {
77
-    if (localStorage.getItem("latitude") || localStorage.getItem("longitude") || localStorage.getItem("timestamp")) {
78
-        var la = localStorage.getItem("latitude");
79
-        var lo = localStorage.getItem("longitude");
80
-        var ti = localStorage.getItem("timestamp");
81
-        var co = { "latitude": la, "longitude": lo };
82
-        var pos = { "coords": co, "timestamp": ti };
83
-        // console.log("Stored location " + pos.co.la + ',' + pos.co.lo);
84
-        return pos;
127
+function getLocation(first) {
128
+
129
+  if (first == false) {
130
+    if (localStorage.getItem("lastlatitude") || localStorage.getItem("lastlongitude") || localStorage.getItem("lasttimestamp")) {
131
+      var la = localStorage.getItem("lastlatitude");
132
+      var lo = localStorage.getItem("lastlongitude");
133
+      var ti = localStorage.getItem("lasttimestamp");
134
+      var co = { "latitude": la, "longitude": lo };
135
+      var pos = { "coords": co, "timestamp": ti };
136
+      return pos;
137
+    } else {
138
+      return null;
139
+    }
140
+  } else {
141
+    if (localStorage.getItem("firstlatitude") || localStorage.getItem("firstlongitude") || localStorage.getItem("firsttimestamp")) {
142
+      var la = localStorage.getItem("firstlatitude");
143
+      var lo = localStorage.getItem("firstlongitude");
144
+      var ti = localStorage.getItem("firsttimestamp");
145
+      var co = { "latitude": la, "longitude": lo };
146
+      var pos = { "coords": co, "timestamp": ti };
147
+      return pos;
85 148
     } else {
86
-        return null;
149
+      return null;
87 150
     }
151
+  }
152
+
153
+
88 154
 }
89 155
 
90 156
 // Get max speed of the run
91 157
 //
92 158
 function getMaxSpeed(lastSpeed) {
93
-    oldmax = localStorage.getItem("maxSpeed") || -1;
94
-    if (oldmax < lastSpeed) {
95
-        maxSpeed = lastSpeed
96
-    } else if (oldmax > lastSpeed) {
97
-        maxSpeed = oldmax
98
-    } else {
99
-        maxSpeed = oldmax
100
-    }
101
-    localStorage.setItem("maxSpeed", maxSpeed);
102
-    return maxSpeed
159
+  oldmax = localStorage.getItem("maxSpeed") || -1;
160
+  if (oldmax < lastSpeed) {
161
+    maxSpeed = lastSpeed
162
+  } else if (oldmax > lastSpeed) {
163
+    maxSpeed = oldmax
164
+  } else {
165
+    maxSpeed = oldmax
166
+  }
167
+  localStorage.setItem("maxSpeed", maxSpeed);
168
+  return maxSpeed
103 169
 }
104 170
 
105 171
 // split float number into an array of int (null returned instead of 0 for decimal)
106 172
 //
107 173
 function splitFloatNumber(num) {
108
-    const intStr = num.toString().split('.')[0];
109
-    const decimalStr = num.toString().split('.')[1];
110
-    return [Number(intStr), Number(decimalStr)];
174
+  const intStr = num.toString().split('.')[0];
175
+  var decimalStr = num.toString().split('.')[1];
176
+  if (decimalStr === undefined) { decimalStr = 0 } else { decimalStr = decimalStr }
177
+  return [Number(intStr), Number(decimalStr)];
111 178
 
112 179
 }
113 180
 
114 181
 // Build GPX headers
115 182
 //
116 183
 function GPXHeadersBuilder(timestamp, name, type) {
117
-    var headers = '<?xml version="1.0" encoding="UTF-8"?><gpx creator="Pebble with barometer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" version="1.1" xmlns="http://www.topografix.com/GPX/1/1"><metadata><time>' + timestamp + '</time></metadata><trk><name>' + name + '</name><type>' + type + '</type><trkseg>';
118
-    var ret = localStorage.setItem("GPX", headers);
119
-    return true;
184
+  var headers = '<?xml version="1.0" encoding="UTF-8"?><gpx creator="Pebble with barometer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" version="1.1" xmlns="http://www.topografix.com/GPX/1/1"><metadata><time>' + timestamp + '</time></metadata><trk><name>' + name + '</name><type>' + type + '</type><trkseg>';
185
+  localStorage.setItem("GPX", headers);
186
+  return true;
120 187
 }
121 188
 
122 189
 // Build GPX track point
123 190
 //
124 191
 function GPXtrkptBuilder(lat, lon, ele, timestamp) {
125
-    var GPX = localStorage.getItem("GPX");
126
-    var trkpt = '<trkpt lat="' + lat + '" lon="' + lon + '"><ele>' + ele + '</ele><time>' + timestamp + '</time></trkpt>';
127
-    localStorage.setItem("GPX", GPX + trkpt);
128
-    return true;
192
+  var GPX = localStorage.getItem("GPX");
193
+  var trkpt = '<trkpt lat="' + lat + '" lon="' + lon + '"><ele>' + ele + '</ele><time>' + timestamp + '</time></trkpt>';
194
+  localStorage.setItem("GPX", GPX + trkpt);
195
+  return true;
129 196
 }
130 197
 
131 198
 // Build GPX footer
132 199
 //
133 200
 function GPXfooterBuilder() {
134
-    var GPX = localStorage.getItem("GPX");
135
-    var footer = '</trkseg></trk></gpx>';
136
-    var ret = localStorage.setItem("GPX", GPX + footer);
137
-    //console.log("GPX closed : " + GPX + footer);
138
-    return ret;
201
+  var GPX = localStorage.getItem("GPX");
202
+  var footer = '</trkseg></trk></gpx>';
203
+  var ret = localStorage.setItem("GPX", GPX + footer);
204
+  //console.log("GPX closed : " + GPX + footer);
205
+  return ret;
139 206
 }
140 207
 
141 208
 //------------------------------------------
... ...
@@ -143,397 +210,438 @@ function GPXfooterBuilder() {
143 210
 //------------------------------------------
144 211
 
145 212
 function getTokens(code) {
146
-    // call to strava api to get tokens in exchange of temp code
147
-    // need to use strava.jonget.fr to proxy request and hide secret
148
-    var url = "https://www.strava.com/oauth/token?client_id=" + client_id + "&client_secret=" + client_secret + "&code=" + code + "&grant_type=authorization_code";
149
-    var xhr = new XMLHttpRequest();
150
-    xhr.timeout = 10000; // time in milliseconds
151
-
152
-    xhr.open("POST", url, false);
153
-
154
-    xhr.onload = function() {
155
-        //console.log('------xhr onloaded')
156
-        if (xhr.readyState === 4) {
157
-            console.log('------xhr request returned :', xhr.responseText);
158
-            response_json = JSON.parse(xhr.responseText);
159
-
160
-            var tokenjson = {
161
-                access_token: response_json.access_token,
162
-                refresh_token: response_json.refresh_token,
163
-                expiry: response_json.expires_at
164
-            };
165
-            localStorage.setItem("strava_tokens", JSON.stringify(tokenjson));
213
+  // call to strava api to get tokens in exchange of temp code
214
+  // need to use strava.jonget.fr to proxy request and hide secret
215
+  var url = "https://www.strava.com/oauth/token?client_id=" + client_id + "&client_secret=" + client_secret + "&code=" + code + "&grant_type=authorization_code";
216
+  var xhr = new XMLHttpRequest();
217
+  xhr.timeout = 10000; // time in milliseconds
218
+
219
+  xhr.open("POST", url, false);
220
+
221
+  xhr.onload = function () {
222
+    //console.log('------xhr onloaded')
223
+    if (xhr.readyState === 4) {
224
+      console.log('------xhr request returned :', xhr.responseText);
225
+      response_json = JSON.parse(xhr.responseText);
226
+
227
+      var tokenjson = {
228
+        access_token: response_json.access_token,
229
+        refresh_token: response_json.refresh_token,
230
+        expiry: response_json.expires_at,
231
+        delay: response_json.expires_in
232
+      };
233
+      localStorage.setItem("strava_tokens", JSON.stringify(tokenjson));
166 234
 
167
-        }
168
-    };
235
+    }
236
+  };
169 237
 
170
-    xhr.send();
238
+  xhr.send();
171 239
 
172 240
 }
173 241
 
174 242
 function refreshTokens(refresh_token) {
175
-    // call to strava api to get tokens in exchange of refresh code
176
-    // need to use strava.jonget.fr to proxy request and hide secret
177
-    var url = "https://www.strava.com/oauth/token?client_id=" + client_id + "&client_secret=" + client_secret + "&refresh_token=" + refresh_token + "&grant_type=refresh_token";
178
-    var xhr = new XMLHttpRequest();
179
-    xhr.timeout = 10000; // time in milliseconds
180
-
181
-    xhr.open("POST", url, false);
182
-
183
-    xhr.onload = function() {
184
-        //console.log('------xhr onloaded')
185
-        if (xhr.readyState === 4) {
186
-            //console.log('------xhr request returned with ' + xhr.status);
187
-            response_json = JSON.parse(xhr.responseText);
188
-
189
-            var tokenjson = {
190
-                access_token: response_json.access_token,
191
-                refresh_token: response_json.refresh_token,
192
-                expiry: response_json.expires_at
193
-            };
194
-            localStorage.setItem("strava_tokens", JSON.stringify(tokenjson));
243
+  // call to strava api to get tokens in exchange of refresh code
244
+  // need to use strava.jonget.fr to proxy request and hide secret
245
+  var url = "https://www.strava.com/oauth/token?client_id=" + client_id + "&client_secret=" + client_secret + "&refresh_token=" + refresh_token + "&grant_type=refresh_token";
246
+  var xhr = new XMLHttpRequest();
247
+  xhr.timeout = 10000; // time in milliseconds
248
+
249
+  xhr.open("POST", url, false);
250
+
251
+  xhr.onload = function () {
252
+    //console.log('------Refresh token - xhr onloaded')
253
+    if (xhr.readyState === 4) {
254
+      console.log('------Refresh token - xhr request returned with ' + xhr.responseText);
255
+      response_json = JSON.parse(xhr.responseText);
256
+
257
+      var tokenjson = {
258
+        access_token: response_json.access_token,
259
+        refresh_token: response_json.refresh_token,
260
+        expiry: response_json.expires_at,
261
+        delay: response_json.expires_in
262
+      };
263
+      localStorage.setItem("strava_tokens", JSON.stringify(tokenjson));
195 264
 
196
-        }
197
-    };
265
+    }
266
+  };
198 267
 
199
-    xhr.send();
268
+  xhr.send();
200 269
 
201 270
 }
202 271
 
203 272
 // Send GPX to Strava profile
204 273
 function SendToStrava() {
205
-    console.log('--- GPX upload to strava');
206
-    var gpxfile = localStorage.getItem("GPX");
207
-
208
-    var tokens = localStorage.getItem("strava_tokens");
209
-
210
-    var bearer = JSON.parse(tokens).access_token;
211
-    params = {
212
-        url: "https://www.strava.com/api/v3/uploads",
213
-        method: "POST",
214
-        data: { description: "desc", data_type: "gpx" },
215
-        files: { file: gpxfile },
216
-        authorization: "Bearer " + bearer,
217
-        callback: function(e) {
218
-            var message = "";
219
-            if (console.log(e.status + " - " + e.txt), 201 == e.status) {
220
-                message = "Your activity has been created";
221
-                localStorage.setItem("strava_uploaded", true);
222
-            } else if (400 == e.status) {
223
-                message = "An error has occurred. If you've already uploaded the current activity, please delete it in Strava.";
224
-            } else if (401 == e.status) {
225
-                message = "Error - Unauthorized. Please check your credentials in the settings."
226
-            } else {
227
-                try {
228
-                    response_json = JSON.parse(e.txt)
229
-                    response_json.error ? (console.log("error:" + response_json.error), message = response_json.error) : response_json.status && (console.log("status:" + response_json.status))
230
-                } catch (e) {
231
-                    console.log("Error log, " + e)
232
-                }
233
-            }
234
-            message && Pebble.showSimpleNotificationOnPebble("Ventoo SE - Strava", message)
235
-        }
236
-    }
237
-    var XHR = new XMLHttpRequest;
238
-    var n = this;
239
-    console.log(params.url);
240
-    XHR.open(params.method, params.url, !0);
241
-    var body = "";
242
-    var boundary = Math.random().toString().substring(2);
243
-    XHR.setRequestHeader("content-type", "multipart/form-data; charset=utf-8; boundary=" + boundary)
244
-    XHR.setRequestHeader("Authorization", params.authorization);
245
-    for (var i in params.data) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '"\r\n\r\n' + params.data[i] + "\r\n";
246
-    for (var i in params.files) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '" ; filename=test.gpx\r\n\r\n' + params.files[i] + "\r\n";
247
-    body += "--" + boundary + "--\r\n"
248
-
249
-    XHR.onreadystatechange = function() {
274
+  console.log('--- GPX upload to strava');
275
+  var gpxfile = localStorage.getItem("GPX");
276
+
277
+  var tokens = localStorage.getItem("strava_tokens");
278
+
279
+  //checking token expiry
280
+  var date = (Date.now()) / 1000
281
+
282
+  if (JSON.parse(tokens).expiry < date) {
283
+    console.log("Strava oAuth token expired, refreshing it")
284
+    refreshTokens(JSON.parse(tokens).refresh_token);
285
+  } else {
286
+    console.log("token (" + JSON.parse(tokens).access_token + ") valid, continuing")
287
+  }
288
+
289
+
290
+  var bearer = JSON.parse(tokens).access_token;
291
+  params = {
292
+    url: "https://www.strava.com/api/v3/uploads",
293
+    method: "POST",
294
+    data: { description: "desc", data_type: "gpx" },
295
+    files: { file: gpxfile },
296
+    authorization: "Bearer " + bearer,
297
+    callback: function (e) {
298
+      var message = "";
299
+      if (console.log(e.status + " - " + e.txt), 201 == e.status) {
300
+        message = "Your activity has been created";
301
+        localStorage.setItem("strava_uploaded", true);
302
+      } else if (400 == e.status) {
303
+        message = "An error has occurred. If you've already uploaded the current activity, please delete it in Strava.";
304
+      } else if (401 == e.status) {
305
+        message = "Error - Unauthorized. Please check your credentials in the settings."
306
+      } else {
250 307
         try {
251
-            4 == XHR.readyState && (n.status = XHR.status, n.txt = XHR.responseText, n.xml = XHR.responseXML, params.callback && params.callback(n))
308
+          response_json = JSON.parse(e.txt)
309
+          response_json.error ? (console.log("error:" + response_json.error), message = response_json.error) : response_json.status && (console.log("status:" + response_json.status))
252 310
         } catch (e) {
253
-            console.error("Error2 loading, ", e)
311
+          console.log("Error log, " + e)
254 312
         }
313
+      }
314
+      message && Pebble.showSimpleNotificationOnPebble("Ventoo SE - Strava", message)
315
+    }
316
+  }
317
+  var XHR = new XMLHttpRequest;
318
+  var n = this;
319
+  //console.log(params.url);
320
+  XHR.open(params.method, params.url, !0);
321
+  var body = "";
322
+  var boundary = Math.random().toString().substring(2);
323
+  XHR.setRequestHeader("content-type", "multipart/form-data; charset=utf-8; boundary=" + boundary)
324
+  XHR.setRequestHeader("Authorization", params.authorization);
325
+  for (var i in params.data) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '"\r\n\r\n' + params.data[i] + "\r\n";
326
+  for (var i in params.files) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '" ; filename=test.gpx\r\n\r\n' + params.files[i] + "\r\n";
327
+  body += "--" + boundary + "--\r\n"
328
+
329
+  XHR.onreadystatechange = function () {
330
+    try {
331
+      4 == XHR.readyState && (n.status = XHR.status, n.txt = XHR.responseText, n.xml = XHR.responseXML, params.callback && params.callback(n))
332
+    } catch (e) {
333
+      console.error("Error2 loading, ", e)
255 334
     }
256
-    XHR.send(body)
335
+  }
336
+  XHR.send(body)
257 337
 }
258 338
 
259 339
 
260 340
 // Send GPX to web server (need configuration on serverside)
261 341
 // TODO : secure it ?
262 342
 function PostToWeb() {
263
-    console.log('--- GPX upload to custom web server');
264
-    var GPX = localStorage.getItem("GPX");
265
-    var url = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_url + "?name=pebblegpx&type=application/gpx+xml";
266
-    var xhr = new XMLHttpRequest();
267
-    xhr.timeout = 10000; // time in milliseconds
268
-
269
-    xhr.open("POST", url, false);
270
-
271
-    //console.log('------ CSV / xhr opened')
272
-    xhr.onload = function() {
273
-        //console.log('------xhr onloaded')
274
-        if (xhr.readyState === 4) {
275
-            //console.log('------xhr request returned with ' + xhr.status);
276
-            //console.log(this.responseText);
277
-            localStorage.setItem("custom_uploaded", true);
278
-            if (xhr.status == 200) {
279
-                //console.log('--> HTTP 200');
280
-                return true;
281
-            } else {
282
-                //console.log('--> HTTP ' + xhr.status);
283
-                return false;
284
-            }
285
-        }
286
-    };
343
+  console.log('--- GPX upload to custom web server');
344
+  var GPX = localStorage.getItem("GPX");
345
+  var url = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_url + "?name=pebblegpx&type=application/gpx+xml";
346
+  var xhr = new XMLHttpRequest();
347
+  xhr.timeout = 10000; // time in milliseconds
348
+
349
+  xhr.open("POST", url, false);
350
+
351
+  //console.log('------ CSV / xhr opened')
352
+  xhr.onload = function () {
353
+    //console.log('------xhr onloaded')
354
+    if (xhr.readyState === 4) {
355
+      //console.log('------xhr request returned with ' + xhr.status);
356
+      //console.log(this.responseText);
357
+      localStorage.setItem("custom_uploaded", true);
358
+      if (xhr.status == 200) {
359
+        //console.log('--> HTTP 200');
360
+        return true;
361
+      } else {
362
+        //console.log('--> HTTP ' + xhr.status);
363
+        return false;
364
+      }
365
+    }
366
+  };
287 367
 
288
-    //send CSV in body
289
-    xhr.send(GPX);
368
+  //send CSV in body
369
+  xhr.send(GPX);
290 370
 
291 371
 }
292 372
 
293 373
 // Send location to web server for instant location (no live tracking)
294 374
 // TODO : secure it ?
295 375
 function instantLocationUpdate(pos) {
296
-    console.log('--- Instant location update');
297
-    // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy);
298
-
299
-    var url = JSON.parse(localStorage.getItem('clay-settings')).ping_location_url + "?lat=" + pos.coords.latitude + "&long=" + pos.coords.longitude + "&acc=" + pos.coords.accuracy + "&timestamp=" + pos.timestamp;
300
-    var xhr = new XMLHttpRequest();
301
-    xhr.timeout = 10000; // time in milliseconds
302
-
303
-    xhr.open("POST", url);
304
-
305
-    //console.log('------ instant / xhr opened')
306
-    xhr.onload = function() {
307
-        //console.log('------xhr onloaded')
308
-        if (xhr.readyState === 4) {
309
-            //console.log('------xhr request returned with ' + xhr.status);
310
-            //console.log(this.responseText);
311
-            if (xhr.status == 200) {
312
-                //console.log('--> HTTP 200');
313
-                return true;
314
-            } else {
315
-                //console.log('--> HTTP ' + xhr.status);
316
-                return false;
317
-            }
318
-        }
319
-    };
376
+  console.log('--- Instant location update');
377
+  // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy);
378
+
379
+  var url = JSON.parse(localStorage.getItem('clay-settings')).ping_location_url + "?lat=" + pos.coords.latitude + "&long=" + pos.coords.longitude + "&acc=" + pos.coords.accuracy + "&timestamp=" + pos.timestamp;
380
+  var xhr = new XMLHttpRequest();
381
+  xhr.timeout = 10000; // time in milliseconds
382
+
383
+  xhr.open("POST", url);
384
+
385
+  //console.log('------ instant / xhr opened')
386
+  xhr.onload = function () {
387
+    //console.log('------xhr onloaded')
388
+    if (xhr.readyState === 4) {
389
+      //console.log('------xhr request returned with ' + xhr.status);
390
+      //console.log(this.responseText);
391
+      if (xhr.status == 200) {
392
+        //console.log('--> HTTP 200');
393
+        return true;
394
+      } else {
395
+        //console.log('--> HTTP ' + xhr.status);
396
+        return false;
397
+      }
398
+    }
399
+  };
320 400
 
321
-    //send without body
322
-    xhr.send();
401
+  //send without body
402
+  xhr.send();
323 403
 
324 404
 }
325 405
 
326 406
 // called in case of successful geoloc gathering and sends the coordinate to watch
327 407
 //
328 408
 function locationSuccess(new_pos) {
329
-    console.log('--- locationSuccess');
330
-    // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy);
331
-
332
-    var prev_pos = getLocation();
333
-    storeLocation(new_pos);
334
-    if (prev_pos === null) {
335
-        console.log('--- start building gpx');
336
-
337
-
338
-        // Start the GPX file
339
-        GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "test", "18");
409
+  console.log('--- locationSuccess');
410
+  // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy);
411
+
412
+  var prev_pos = getLocation(false);
413
+  var first_pos = getLocation(true);
414
+  if (prev_pos === null) {
415
+    console.log('--- start building gpx');
416
+    storeLocation(new_pos, true);
417
+    localStorage.setItem("strava_uploaded", false);
418
+    localStorage.setItem("custom_uploaded", false);
419
+
420
+    // Start the GPX file
421
+    GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "test", "18");
422
+
423
+  } else {
424
+    storeLocation(new_pos, false);
425
+
426
+    // Prepare display on watch
427
+    // now it's only raw data
428
+    // init strings
429
+    var latitudeString = "";
430
+    var longitudeString = "";
431
+    var accuracyString = "";
432
+    var altitudeString = "";
433
+    var speedString = "";
434
+    var distanceString = "";
435
+
436
+    // get speed from geoloc API isntead of calculate it
437
+    // speed is initially in m/s, get it at km/h
438
+    if (new_pos.coords.speed === null) {
439
+      var speed = 0;
440
+    } else {
441
+      var speed = new_pos.coords.speed * 3.6;
442
+      localStorage.setItem("speedsum", parseInt(localStorage.getItem("speedsum")) + speed);
443
+    }
340 444
 
445
+    // distance since beginning in m
446
+    var dist = distance_on_geoid(prev_pos.coords.latitude, prev_pos.coords.longitude, new_pos.coords.latitude, new_pos.coords.longitude);
447
+    var totaldist = parseInt(localStorage.getItem("dist"));
448
+    if (!isNaN(dist)) {
449
+      totaldist = totaldist + parseInt(dist);
450
+      localStorage.setItem("dist", totaldist);
451
+    }
452
+    distanceString = splitFloatNumber(totaldist / 1000)[0].toString();
453
+    //console.log("total dist is now " + totaldist);
454
+
455
+    // avg speed (also when not moving) since beginning
456
+    var avgspeed = parseInt(localStorage.getItem("speedsum")) / parseInt(localStorage.getItem("totalcoordinates"));
457
+    var avgSpeedString = splitFloatNumber(avgspeed)[0].toString() + "." + splitFloatNumber(avgspeed)[1].toString().substring(0, 1);
458
+    console.log("speedsum=" + parseInt(localStorage.getItem("speedsum")) + " / totalcoordinates=" + parseInt(localStorage.getItem("totalcoordinates")));
459
+    console.log("--avgspeed=" + avgspeed + " / avgSpeedString=" + avgSpeedString)
460
+    if (avgSpeedString == "0.N") {
461
+      avgSpeedString = "0.0";
462
+    }
463
+    //console.log("avg speed is : " + avgSpeedString);
464
+
465
+    // Duration
466
+
467
+    var duration = new_pos.timestamp - first_pos.timestamp;
468
+    const date = new Date(duration);
469
+    durationString = padStart(date.getUTCHours().toString(), 2, "0") + ":" + padStart(date.getMinutes().toString(), 2, "0") + ":" + padStart(date.getSeconds().toString(), 2, "0");
470
+    console.log("durationString is : " + durationString);
471
+
472
+    //formating for precision and max size
473
+    latitudeString = new_pos.coords.latitude.toString().substring(0, 12);
474
+    longitudeString = new_pos.coords.longitude.toString().substring(0, 12);
475
+    accuracyString = new_pos.coords.accuracy.toString().substring(0, 4);
476
+    //console.log("split num : " + new_pos.coords.altitude);
477
+    altitudeString = splitFloatNumber(new_pos.coords.altitude)[0].toString().substring(0, 5);
478
+    timestampISO = new Date(new_pos.timestamp).toISOString();
479
+
480
+    //console.log("split num : " + speed);
481
+    if (isNaN(speed)) {
482
+      speedString = "---";
341 483
     } else {
342
-        //clear watch of new position to avoid overlap
343
-        //navigator.geolocation.clearWatch(geoloc_id);
344
-        //console.log('--- watch geoloc cleared');
345
-        //var speed = speed_from_distance_and_time(prev_pos, new_pos);
346
-
347
-        //get speed from geoloc API isntead of calculate it
348
-        // speed is initially in m/s, get it at km/h
349
-        if (new_pos.coords.speed === null) {
350
-            var speed = 0;
351
-        } else {
352
-            var speed = new_pos.coords.speed * 3.6;
353
-        }
484
+      speedString = splitFloatNumber(speed)[0].toString().substring(0, 3) + "." + splitFloatNumber(speed)[1].toString().substring(0, 1);
485
+      if (speedString == "0.N") {
486
+        speedString = "0.0";
487
+      }
354 488
 
355
-        // Prepare display on watch
356
-        // now it's only raw data
357
-        // init strings
358
-        var latitudeString = "";
359
-        var longitudeString = "";
360
-        var accuracyString = "";
361
-        var altitudeString = "";
362
-        var speedString = "";
363
-
364
-        //formating for precision and max size
365
-        latitudeString = new_pos.coords.latitude.toString().substring(0, 12);
366
-        longitudeString = new_pos.coords.longitude.toString().substring(0, 12);
367
-        accuracyString = new_pos.coords.accuracy.toString().substring(0, 4);
368
-        //console.log("split num : " + new_pos.coords.altitude);
369
-        altitudeString = splitFloatNumber(new_pos.coords.altitude)[0].toString().substring(0, 5);
370
-        timestampISO = new Date(new_pos.timestamp).toISOString();
371
-        //console.log("split num : " + speed);
372
-        if (isNaN(speed)) {
373
-            speedString = "---";
374
-        } else {
375
-            speedString = splitFloatNumber(speed)[0].toString().substring(0, 3) + "." + splitFloatNumber(speed)[1].toString().substring(0, 1);
376
-            if (speedString == "0.N") {
377
-                speedString = "0.0";
378
-            }
489
+      //console.log("split num : " + getMaxSpeed(speed));
490
+      maxSpeedString = splitFloatNumber(getMaxSpeed(speed))[0].toString().substring(0, 3);
491
+    }
379 492
 
380
-            //console.log("split num : " + getMaxSpeed(speed));
381
-            maxSpeedString = splitFloatNumber(getMaxSpeed(speed))[0].toString().substring(0, 3);
382
-        }
493
+    //add a new datapoint to GPX file
494
+    GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO);
383 495
 
384
-        //add a new datapoint to GPX file
385
-        GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO);
386 496
 
387 497
 
498
+    // Build message
499
+    message = "OK";
500
+    var dict = {
501
+      'accuracy': accuracyString,
502
+      'distance': distanceString,
503
+      'avg_speed': avgSpeedString,
504
+      'duration': durationString,
505
+      'altitude': altitudeString,
506
+      'speed': speedString,
507
+      'max_speed': maxSpeedString,
508
+      'status': message
509
+    };
388 510
 
389
-        // Build message
390
-        message = "OK";
391
-        var dict = {
392
-            'accuracy': accuracyString,
393
-            'altitude': altitudeString,
394
-            'speed': speedString,
395
-            'max_speed': maxSpeedString,
396
-            'status': message
397
-        };
511
+    // Send the message
512
+    Pebble.sendAppMessage(dict, function () {
513
+      console.log('Message sent successfully: ' + JSON.stringify(dict));
514
+    }, function (e) {
515
+      console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e));
516
+    });
517
+  }
398 518
 
399
-        // Send the message
400
-        Pebble.sendAppMessage(dict, function() {
401
-            console.log('Message sent successfully: ' + JSON.stringify(dict));
402
-        }, function(e) {
403
-            console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e));
404
-        });
405
-    }
406 519
 
407 520
 }
408 521
 
409 522
 function locationError(err) {
410 523
 
411
-    console.warn('location error (' + err.code + '): ' + err.message);
524
+  console.warn('location error (' + err.code + '): ' + err.message);
412 525
 
413 526
 }
414 527
 
415 528
 
416
-function get_coordinate() {
417
-    console.log('--- Starting regular getCurrentPosition loop using setInterval at 1 sec');
418
-
419
-    locationInterval = setInterval(function() {
420
-        navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
421
-    }, 1000);
529
+function start_get_coordinate() {
530
+  clearInterval(firstlocationInterval);
531
+  firstlocationInterval = false;
532
+  locationInterval = setInterval(function () {
533
+    navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
534
+  }, 1000);
422 535
 
423
-    if (locate_me) {
424
-        instantLocationInterval = setInterval(function() {
425
-            navigator.geolocation.getCurrentPosition(instantLocationUpdate, locationError, locationOptions);
426
-        }, 60000);
427
-    }
536
+  if (locate_me) {
537
+    instantLocationInterval = setInterval(function () {
538
+      navigator.geolocation.getCurrentPosition(instantLocationUpdate, locationError, locationOptions);
539
+    }, 60000);
540
+  }
428 541
 
429 542
 }
430 543
 
431 544
 function init() {
432
-    // local storage init
433
-    try {
434
-        //console.log("Clay settings = " + localStorage.getItem('clay-settings'));
435
-        gpx_to_strava = JSON.parse(localStorage.getItem('clay-settings')).strava_enabled;
436
-        gpx_to_web = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_enabled;
437
-        locate_me = JSON.parse(localStorage.getItem('clay-settings')).ping_location_enabled;
438
-
439
-        console.log("Locate_me = " + locate_me);
440
-        console.log("Strava = " + gpx_to_strava);
441
-        console.log("Custom web = " + gpx_to_web);
442
-
443
-        var ce = gpx_to_web;
444
-        var cu = localStorage.getItem("custom_uploaded");
445
-        var se = gpx_to_strava;
446
-        var su = localStorage.getItem("strava_uploaded");
447
-
448
-        //checking token freshness (expiry >4h)
449
-        var delay = (Date.now() + 14400000) / 1000
450
-        if (se) {
451
-            if (JSON.parse(localStorage.getItem("strava_tokens")).expiry < delay) {
452
-                console.log("Strava oAuth token expiring or expired, refreshing it")
453
-                refreshTokens(JSON.parse(localStorage.getItem("strava_tokens")).refresh_token);
454
-            } else {
455
-                console.log("token (" + JSON.parse(localStorage.getItem("strava_tokens")).access_token + ")valid for 4h min, continuing")
456
-            }
457
-        }
458
-    } catch (e) {}
459 545
 
460
-    if ((se && !su) || (ce && !cu)) {
461
-        //posting any missed XHR from previous ride session
462
-        if (ce) {
463
-            PostToWeb();
464
-        }
465
-        if (se) {
466
-            SendToStrava();
467
-        }
468
-    } else {
469
-        localStorage.setItem("GPX", "");
470
-        localStorage.setItem("maxSpeed", "");
471
-        localStorage.setItem("latitude", "");
472
-        localStorage.setItem("longitude", "");
473
-        localStorage.setItem("timestamp", "");
474
-        localStorage.setItem("custom_uploaded", false);
475
-        localStorage.setItem("strava_uploaded", false);
476
-    }
477
-    // clear any other var to do
478
-    clearInterval(locationInterval);
479
-    clearInterval(instantLocationInterval);
546
+  clearInterval(locationInterval);
547
+  locationInterval = false;
548
+  clearInterval(instantLocationInterval);
549
+  instantLocationInterval = false;
550
+  firstlocationInterval = setInterval(function () {
480 551
     navigator.geolocation.getCurrentPosition(null, locationError, firstlocationOptions);
552
+  }, 1000);
553
+
554
+  //console.log("Clay settings = " + localStorage.getItem('clay-settings'));
555
+  var se = JSON.parse(localStorage.getItem('clay-settings')).strava_enabled;
556
+  var su = ("true" === localStorage.getItem("strava_uploaded"));
557
+  var ce = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_enabled;
558
+  var cu = ("true" === localStorage.getItem("custom_uploaded"));
559
+
560
+  locate_me = JSON.parse(localStorage.getItem('clay-settings')).ping_location_enabled;
561
+
562
+
563
+  console.log("Locate_me = " + locate_me);
564
+  console.log("Strava = " + se + " (" + typeof se + ")/ uploaded = " + su + " (" + typeof su + ")");
565
+  console.log("Custom web = " + ce + " (" + typeof ce + ")/ uploaded = " + cu + " (" + typeof cu + ")");
566
+
567
+  if ((se && !su) || (ce && !cu)) {
568
+    if (se) {
569
+      console.log("GPX upload needed to Strava")
570
+      SendToStrava();
571
+    }
572
+
573
+    if (ce) {
574
+      console.log("GPX upload needed to custom server")
575
+      PostToWeb();
576
+    }
577
+  } else {
578
+    console.log("clearing var")
579
+    localStorage.setItem("maxSpeed", 0);
580
+    localStorage.setItem("firstlatitude", "");
581
+    localStorage.setItem("firstlongitude", "");
582
+    localStorage.setItem("firsttimestamp", "");
583
+    localStorage.setItem("lastlatitude", "");
584
+    localStorage.setItem("lastlongitude", "");
585
+    localStorage.setItem("lasttimestamp", "");
586
+    localStorage.setItem("dist", 0)
587
+    localStorage.setItem("speedsum", 0)
588
+  }
481 589
 
482 590
 }
483 591
 
484 592
 // Get JS readiness events
485
-Pebble.addEventListener('ready', function(e) {
486
-    console.log('PebbleKit JS is ready');
487
-    // Update Watch on this
488
-    Pebble.sendAppMessage({ 'JSReady': 1 });
593
+Pebble.addEventListener('ready', function (e) {
594
+  console.log('PebbleKit JS is ready');
595
+  // Update Watch on this
596
+  Pebble.sendAppMessage({ 'JSReady': 1 });
489 597
 
490
-    init();
598
+  init();
491 599
 });
492 600
 
493 601
 // Get AppMessage events
494
-Pebble.addEventListener('appmessage', function(e) {
495
-    // Get the dictionary from the message
496
-    var dict = e.payload;
497
-    //console.log(dict[0].toString());
498
-    switch (dict[0]) {
499
-        case 'get':
500
-            get_coordinate();
501
-            break;
502
-        case 'exit':
503
-            clearInterval(instantLocationInterval);
504
-            clearInterval(locationInterval);
505
-            gpx_to_strava = JSON.parse(localStorage.getItem('clay-settings')).strava_enabled;
506
-            gpx_to_web = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_enabled;
507
-            locate_me = JSON.parse(localStorage.getItem('clay-settings')).ping_location_enabled;
508
-
509
-            //End GPX file
510
-            GPXfooterBuilder();
511
-            if (gpx_to_strava) {
512
-                //send to strava through API
513
-                SendToStrava();
514
-            }
515
-            if (gpx_to_web) {
516
-                // send CSV to web server through API
517
-                PostToWeb();
518
-            }
519
-            if (locate_me) {
520
-                var prev_pos = getLocation();
521
-                instantLocationUpdate(prev_pos);
522
-            }
523
-
524
-            // Send the message
525
-            var dict = {
526
-                'status': "EXIT"
527
-            };
528
-            Pebble.sendAppMessage(dict, function() {
529
-                console.log('Message sent successfully: ' + JSON.stringify(dict));
530
-            }, function(e) {
531
-                console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e));
532
-            });
533
-
534
-            break;
535
-        default:
536
-            console.log('Sorry. I don\'t understand your request :' + dict[0]);
537
-    }
602
+Pebble.addEventListener('appmessage', function (e) {
603
+  // Get the dictionary from the message
604
+  var dict = e.payload;
605
+  //console.log(dict[0].toString());
606
+  switch (dict[0]) {
607
+    case 'startstop':
608
+      if (!locationInterval == false) {
609
+        console.log("Stopping the track");
610
+        clearInterval(locationInterval);
611
+        GPXfooterBuilder();
612
+        locationInterval = false;
613
+        clearInterval(instantLocationInterval);
614
+        instantLocationInterval = false;
615
+        firstlocationInterval = setInterval(function () {
616
+          navigator.geolocation.getCurrentPosition(null, locationError, firstlocationOptions);
617
+        }, 1000);
618
+      } else {
619
+        console.log("Starting the track");
620
+        start_get_coordinate();
621
+      }
622
+      break;
623
+    case 'send':
624
+      if (locate_me) {
625
+        var prev_pos = getLocation(false);
626
+        instantLocationUpdate(prev_pos);
627
+      }
628
+
629
+      init();
630
+
631
+      /*
632
+      // Send the message
633
+      var dict = {
634
+        'status': "EXIT"
635
+      };
636
+      Pebble.sendAppMessage(dict, function () {
637
+        console.log('Message sent successfully: ' + JSON.stringify(dict));
638
+      }, function (e) {
639
+        console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e));
640
+      });
641
+      */
642
+      break;
643
+    default:
644
+      console.log('Sorry. I don\'t understand your request :' + dict[0]);
645
+  }
538 646
 
539 647
 });
540 648
\ No newline at end of file