diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index cf4686c60..91137f70d 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -15,8 +15,7 @@ If you just have a question, please ask first in the user chatroom in Matrix: `#
### I got Gadgetbridge from:
* [ ] F-Droid
* [ ] I built it myself from source code (specify tag / commit)
-
-If you got it from Google Play, please note [that version](https://github.com/TaaviE/Gadgetbridge) is unofficial and not supported here; it's also often quite outdated. Please switch to one of the above versions if you can.
+* [ ] I previously used Gadgetbridge from other sources and then updated to F-Droid version
#### Your issue is:
*If possible, please attach [logs](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Log-Files)! that might help identifying the problem.*
@@ -35,7 +34,7 @@ Here go lines of your log.
*Please specify model and firmware version if possible*
-#### Your android version is:
+#### Your Android version/manufacturer flavor is:
#### Your Gadgetbridge version is:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index cf4686c60..91137f70d 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -15,8 +15,7 @@ If you just have a question, please ask first in the user chatroom in Matrix: `#
### I got Gadgetbridge from:
* [ ] F-Droid
* [ ] I built it myself from source code (specify tag / commit)
-
-If you got it from Google Play, please note [that version](https://github.com/TaaviE/Gadgetbridge) is unofficial and not supported here; it's also often quite outdated. Please switch to one of the above versions if you can.
+* [ ] I previously used Gadgetbridge from other sources and then updated to F-Droid version
#### Your issue is:
*If possible, please attach [logs](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Log-Files)! that might help identifying the problem.*
@@ -35,7 +34,7 @@ Here go lines of your log.
*Please specify model and firmware version if possible*
-#### Your android version is:
+#### Your Android version/manufacturer flavor is:
#### Your Gadgetbridge version is:
diff --git a/.github/ISSUE_TEMPLATE/device_request.md b/.github/ISSUE_TEMPLATE/device_request.md
index a3b1e121a..e6b67390d 100644
--- a/.github/ISSUE_TEMPLATE/device_request.md
+++ b/.github/ISSUE_TEMPLATE/device_request.md
@@ -14,9 +14,15 @@ You can use the `Preview` tab ^ above to see final rendering of your report. Use
#### Device information
+- Adding an implementation for a new device requires a "willing to learn" developer, ideally with the device at hand. Without that, you may try to submit a device request and see if anyone steps up and implements it.
+
+
- Provide device name, manufacturer and similarity to other devices:
+- Ideally, use an Android Bluetooth scanner app like nRF Connect or BLExplorer and provide screenshots of the scanned device from that app. This provides a name and some available UUIDs, which are needed for implementation. You may want to blur a MAC address for privacy reasons.
+
+
- Specify model and firmware version if possible:
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index f2190462b..7283c0b3d 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -28,7 +28,7 @@ Here go lines of your log.
*Please specify model and firmware version if possible*
-#### Your android version is:
+#### Your Android version/manufacturer flavor is:
#### Your Gadgetbridge version is:
diff --git a/.gitmodules b/.gitmodules
index 4c058130a..19b292ea4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "fossil-hr-watchface"]
path = external/fossil-hr-watchface
- url = https://github.com/arjan-s/fossil-hr-watchface
+ url = https://codeberg.org/Freeyourgadget/fossil-hr-watchface
[submodule "jerryscript"]
path = external/jerryscript
url = https://github.com/jerryscript-project/jerryscript
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d0d3b852e..9ec0f27c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
### Changelog
+### 0.66.0
+* Add basic support for Casio GBD-H1000
+* Add support for Hama Fit Track 1900 - via FitPro device support
+* Add OpenTracksController for interactions with OpenTracks
+* Fossil Hybrid HR: Start/stop track in OpenTracks from GPS workout on watch
+* Fossil Hybrid HR: Try guessing new widget position
+* Fossil Hybrid HR: Allow assigning no function to a button
+* Add Huami button/device action to control fitness tracking via OpenTracksController
+* Mi Band 6: Sync alarms set on the watch like on Amazfit Bip U and others
+* Bangle.js: Handle battery charging status and fix battery chart.
+* Bangle.js: Prevent exception in case UART RX line is empty
+* Bangle.js: Add repetitions in alarm JSON
+* WaspOS: Fix battery chart.
+* WaspOS: Add condition code to weather JSON
+* XWatch: Add notifications and calls support
+* UM-25: Make cumulative values resettable
+* VESC: Fixed crash when loading a saved value
+* Allow to open Android notification settings from Notification settings
+* AutoExporter changes for better operation and troubleshooting
+* Change Nightly icons background color
+
### 0.65.0
* Amazfit Pop/Pro: Initial Support (probably the same as Bip U but has a different firmware)
* Sony WH-1000XM4: Initial Support
diff --git a/app/build.gradle b/app/build.gradle
index 5a34e5732..1a03e3b5a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -56,8 +56,8 @@ android {
multiDexEnabled true
// Note: always bump BOTH versionCode and versionName!
- versionName "0.65.0"
- versionCode 209
+ versionName "0.66.0"
+ versionCode 210
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
buildConfigField "String", "GIT_HASH_SHORT", "\"${getGitHashShort()}\""
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 0cd67ca85..6af6c8ff1 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -18,6 +18,10 @@
-keepclassmembers class nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.JSInterface {
public *;
}
+# Required for refection in BangleJSDeviceSupport
+-keepclassmembers class nodomain.freeyourgadget.gadgetbridge.model.CallSpec {
+ public static *;
+}
-keepattributes JavascriptInterface
# https://github.com/tony19/logback-android/issues/29
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fac9decb4..edf7b965e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -668,5 +668,10 @@
+
+
diff --git a/app/src/main/assets/fossil_hr/battery_layout.json b/app/src/main/assets/fossil_hr/battery_layout.json
new file mode 100644
index 000000000..4d35d4c85
--- /dev/null
+++ b/app/src/main/assets/fossil_hr/battery_layout.json
@@ -0,0 +1 @@
+[{"id":0,"type":"complication_background","background":"#background","goal_ring":{"is_enable":"#goal_ring","end_angle":"#fi","is_invert":"#$e"},"dimension":{"type":"rigid","width":"#size.w","height":"#size.h"},"placement":{"type":"absolute","left":"#pos.Ue","top":"#pos.Qe"},"visible":true,"inversion":false},{"id":1,"parent_id":0,"type":"complication_content","icon":"icBattery","text_low":"#ci","dimension":{"type":"rigid","width":76,"height":76},"placement":{"type":"relative"},"visible":true,"inversion":"#$e"},{"id":2,"parent_id":1,"type":"solid","placement":{"type":"absolute","left":29,"top":23},"color":"#nt","dimension":{"type":"rigid","height":6,"width":"#it"},"visible":true,"inversion":false},{"id":3,"parent_id":1,"type":"image","image_name":"icBattCharging","draw_mode":1,"placement":{"type":"absolute","left":34,"top":21},"dimension":{"width":6,"height":9},"visible":"#et","inversion":false}]
diff --git a/app/src/main/assets/fossil_hr/complication_layout.json b/app/src/main/assets/fossil_hr/complication_layout.json
new file mode 100644
index 000000000..42be572d2
--- /dev/null
+++ b/app/src/main/assets/fossil_hr/complication_layout.json
@@ -0,0 +1 @@
+[{"id":0,"type":"complication_background","background":"#background","goal_ring":{"is_enable":"#goal_ring","end_angle":"#fi","is_invert":"#$e"},"dimension":{"type":"rigid","width":"#size.w","height":"#size.h"},"placement":{"type":"absolute","left":"#pos.Ue","top":"#pos.Qe"},"visible":true,"inversion":false},{"id":1,"parent_id":0,"type":"complication_content","icon":"#icon","text_high":"#dt","text_low":"#ci","dimension":{"type":"rigid","width":76,"height":76},"placement":{"type":"relative"},"visible":true,"inversion":"#$e"}]
\ No newline at end of file
diff --git a/app/src/main/assets/fossil_hr/image_layout.json b/app/src/main/assets/fossil_hr/image_layout.json
new file mode 100644
index 000000000..1ed732279
--- /dev/null
+++ b/app/src/main/assets/fossil_hr/image_layout.json
@@ -0,0 +1 @@
+[{"id":0,"type":"container","direction":1,"main_alignment":1,"cross_alignment":1,"dimension":{"type":"rigid","width":240,"height":240},"placement":{"type":"absolute","left":0,"top":0},"visible":true,"inversion":false},{"id":1,"parent_id":0,"type":"image","image_name":"#name","draw_mode":1,"placement":{"type":"absolute","left":"#pos.Ue","top":"#pos.Qe"},"dimension":{"width":"#size.w","height":"#size.h"},"visible":true,"inversion":false}]
\ No newline at end of file
diff --git a/app/src/main/assets/fossil_hr/menu_layout.json b/app/src/main/assets/fossil_hr/menu_layout.json
new file mode 100644
index 000000000..6be3d627d
--- /dev/null
+++ b/app/src/main/assets/fossil_hr/menu_layout.json
@@ -0,0 +1,207 @@
+[
+ {
+ "id": 0,
+ "type": "container",
+ "direction": 1,
+ "main_alignment": 0,
+ "cross_alignment": 1,
+ "dimension": {
+ "type": "rigid",
+ "width": 240,
+ "height": 240
+ },
+ "placement": {
+ "type": "absolute",
+ "left": 0,
+ "top": 0
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 1,
+ "parent_id": 0,
+ "type": "container",
+ "direction": 1,
+ "main_alignment": 1,
+ "cross_alignment": 2,
+ "dimension": {
+ "type": "rigid",
+ "width": 130,
+ "height": 34
+ },
+ "placement": {
+ "type": "absolute",
+ "left": 75,
+ "top": 45
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 2,
+ "parent_id": 1,
+ "type": "text",
+ "text": "#top_short_press_label",
+ "ppem": 17,
+ "color": 3,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 3,
+ "parent_id": 1,
+ "type": "text",
+ "text": "#top_long_press_label",
+ "ppem": 17,
+ "color": 3,
+ "ascent": 17,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 4,
+ "parent_id": 0,
+ "type": "container",
+ "direction": 1,
+ "main_alignment": 1,
+ "cross_alignment": 2,
+ "dimension": {
+ "type": "rigid",
+ "width": 80,
+ "height": 34
+ },
+ "placement": {
+ "type": "absolute",
+ "left": 135,
+ "top": 103
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 5,
+ "parent_id": 4,
+ "type": "text",
+ "text": "#middle_short_press_label",
+ "ppem": 17,
+ "color": 3,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 6,
+ "parent_id": 4,
+ "type": "text",
+ "text": "#middle_long_press_label",
+ "ppem": 17,
+ "color": 3,
+ "ascent": 17,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 7,
+ "parent_id": 0,
+ "type": "container",
+ "direction": 1,
+ "main_alignment": 1,
+ "cross_alignment": 2,
+ "dimension": {
+ "type": "rigid",
+ "width": 130,
+ "height": 34
+ },
+ "placement": {
+ "type": "absolute",
+ "left": 75,
+ "top": 161
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 8,
+ "parent_id": 7,
+ "type": "text",
+ "text": "#bottom_short_press_label",
+ "ppem": 17,
+ "color": 3,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 9,
+ "parent_id": 7,
+ "type": "text",
+ "text": "#bottom_long_press_label",
+ "ppem": 17,
+ "color": 3,
+ "ascent": 17,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 10,
+ "parent_id": 0,
+ "type": "text",
+ "text": "#menu_title",
+ "ppem": 25,
+ "color": 3,
+ "ascent": 35,
+ "placement": {
+ "type": "relative"
+ },
+ "visible": true,
+ "inversion": false
+ },
+ {
+ "id": 11,
+ "parent_id": 0,
+ "type": "text_page",
+ "text": "#message_to_display",
+ "ppem": 17,
+ "color": 3,
+ "ascent": 17,
+ "cross_alignment": 2,
+ "line_width": [
+ 80,
+ 85,
+ 90,
+ 90,
+ 85,
+ 80
+ ],
+ "dimension": {
+ "type": "rigid",
+ "width": 80,
+ "height": 100
+ },
+ "placement": {
+ "type": "absolute",
+ "left": 20,
+ "top": 60
+ },
+ "visible": true,
+ "inversion": false
+ }
+ ]
+
\ No newline at end of file
diff --git a/app/src/main/assets/fossil_hr/openSourceWatchface.bin b/app/src/main/assets/fossil_hr/openSourceWatchface.bin
index e6cbe5917..41e239c15 100644
Binary files a/app/src/main/assets/fossil_hr/openSourceWatchface.bin and b/app/src/main/assets/fossil_hr/openSourceWatchface.bin differ
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java
index 1b95e6d4d..2425b7ec7 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java
@@ -62,11 +62,13 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
+import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothStateChangeReceiver;
+import nodomain.freeyourgadget.gadgetbridge.externalevents.OpenTracksContentObserver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
@@ -143,6 +145,11 @@ public class GBApplication extends Application {
private DeviceManager deviceManager;
private BluetoothStateChangeReceiver bluetoothStateChangeReceiver;
+ private OpenTracksContentObserver openTracksObserver;
+
+ private long lastAutoExportTimestamp = 0;
+ private long autoExportScheduledTimestamp = 0;
+
public static void quit() {
GB.log("Quitting Gadgetbridge...", GB.INFO, null);
Intent quitIntent = new Intent(GBApplication.ACTION_QUIT);
@@ -210,6 +217,8 @@ public class GBApplication extends Application {
loadAppsPebbleBlackList();
loadCalendarsBlackList();
+ PeriodicExporter.enablePeriodicExport(context);
+
if (isRunningMarshmallowOrLater()) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (isRunningOreoOrLater()) {
@@ -1090,4 +1099,28 @@ public class GBApplication extends Application {
return "Gadgetbridge";
}
}
+
+ public void setOpenTracksObserver(OpenTracksContentObserver openTracksObserver) {
+ this.openTracksObserver = openTracksObserver;
+ }
+
+ public OpenTracksContentObserver getOpenTracksObserver() {
+ return openTracksObserver;
+ }
+
+ public long getLastAutoExportTimestamp() {
+ return lastAutoExportTimestamp;
+ }
+
+ public void setLastAutoExportTimestamp(long lastAutoExportTimestamp) {
+ this.lastAutoExportTimestamp = lastAutoExportTimestamp;
+ }
+
+ public long getAutoExportScheduledTimestamp() {
+ return autoExportScheduledTimestamp;
+ }
+
+ public void setAutoExportScheduledTimestamp(long autoExportScheduledTimestamp) {
+ this.autoExportScheduledTimestamp = autoExportScheduledTimestamp;
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java
index 145a9f7c4..2b526cce8 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java
@@ -42,6 +42,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
+import java.util.Date;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@@ -51,6 +52,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
+import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
@@ -145,7 +147,7 @@ public class DataManagementActivity extends AbstractGBActivity {
cleanExportDirectory();
}
});
-
+ GBApplication gbApp = GBApplication.app();
Prefs prefs = GBApplication.getPrefs();
boolean autoExportEnabled = prefs.getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false);
int autoExportInterval = prefs.getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0);
@@ -153,16 +155,42 @@ public class DataManagementActivity extends AbstractGBActivity {
//String autoExportLocation = prefs.getString(GBPrefs.AUTO_EXPORT_LOCATION, "");
int testExportVisibility = (autoExportInterval > 0 && autoExportEnabled) ? View.VISIBLE : View.GONE;
-
+ boolean isExportEnabled = autoExportInterval > 0 && autoExportEnabled;
TextView autoExportLocation_label = findViewById(R.id.autoExportLocation_label);
autoExportLocation_label.setVisibility(testExportVisibility);
- TextView autoExportLocation_intro = findViewById(R.id.autoExportLocation_intro);
- autoExportLocation_intro.setVisibility(testExportVisibility);
-
TextView autoExportLocation_path = findViewById(R.id.autoExportLocation_path);
autoExportLocation_path.setVisibility(testExportVisibility);
- autoExportLocation_path.setText(getAutoExportLocationSummary());
+ autoExportLocation_path.setText(getAutoExportLocationUserString() + " (" + getAutoExportLocationPreferenceString() + ")" );
+
+ TextView autoExportEnabled_label = findViewById(R.id.autoExportEnabled);
+ if (isExportEnabled) {
+ autoExportEnabled_label.setText(getString(R.string.activity_db_management_autoexport_enabled_yes));
+ } else {
+ autoExportEnabled_label.setText(getString(R.string.activity_db_management_autoexport_enabled_no));
+ }
+
+ TextView autoExportScheduled = findViewById(R.id.autoExportScheduled);
+ autoExportScheduled.setVisibility(testExportVisibility);
+ long setAutoExportScheduledTimestamp = gbApp.getAutoExportScheduledTimestamp();
+ if (setAutoExportScheduledTimestamp > 0) {
+ autoExportScheduled.setText(getString(R.string.activity_db_management_autoexport_scheduled_yes,
+ DateTimeUtils.formatDateTime(new Date(setAutoExportScheduledTimestamp))));
+ } else {
+ autoExportScheduled.setText(getResources().getString(R.string.activity_db_management_autoexport_scheduled_no));
+ }
+
+ TextView autoExport_lastTime_label = findViewById(R.id.autoExport_lastTime_label);
+ long lastAutoExportTimestamp = gbApp.getLastAutoExportTimestamp();
+
+ autoExport_lastTime_label.setVisibility(View.GONE);
+ autoExport_lastTime_label.setText(getString(R.string.autoExport_lastTime_label,
+ DateTimeUtils.formatDateTime(new Date(lastAutoExportTimestamp))));
+
+ if (lastAutoExportTimestamp > 0) {
+ autoExport_lastTime_label.setVisibility(testExportVisibility);
+ autoExport_lastTime_label.setVisibility(testExportVisibility);
+ }
final Context context = getApplicationContext();
Button testExportDBButton = findViewById(R.id.testExportDBButton);
@@ -180,18 +208,25 @@ public class DataManagementActivity extends AbstractGBActivity {
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
}
-
-
- //would rather re-use method of SettingsActivity... but lifecycle...
- private String getAutoExportLocationSummary() {
+ private String getAutoExportLocationPreferenceString() {
String autoExportLocation = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null);
if (autoExportLocation == null) {
return "";
}
+ return autoExportLocation;
+ }
+
+ private String getAutoExportLocationUri() {
+ String autoExportLocation = getAutoExportLocationPreferenceString();
+ if (autoExportLocation == null) {
+ return "";
+ }
Uri uri = Uri.parse(autoExportLocation);
try {
+
return AndroidUtils.getFilePath(getApplicationContext(), uri);
} catch (IllegalArgumentException e) {
+ LOG.error("getFilePath did not work, trying to resolve content provider path");
try {
Cursor cursor = getContentResolver().query(
uri,
@@ -208,6 +243,13 @@ public class DataManagementActivity extends AbstractGBActivity {
return "";
}
+ private String getAutoExportLocationUserString() {
+ String location = getAutoExportLocationUri();
+ if (location == "") {
+ return getString(R.string.activity_db_management_autoexport_location);
+ }
+ return location;
+ }
private boolean hasOldActivityDatabase() {
return new DBHelper(this).existsDB("ActivityDatabase");
@@ -403,7 +445,7 @@ public class DataManagementActivity extends AbstractGBActivity {
public void onClick(DialogInterface dialog, int which) {
try {
File externalFilesDir = FileUtils.getExternalFilesDir();
- String autoexportFile = getAutoExportLocationSummary();
+ String autoexportFile = getAutoExportLocationUri();
for (File file : externalFilesDir.listFiles()) {
if (file.isFile() &&
(!FileUtils.getExtension(file.toString()).toLowerCase().equals("gpx")) && //keep GPX files
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
index 38bdc1047..29957ada4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
@@ -35,6 +35,7 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -70,6 +71,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.TreeMap;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@@ -83,6 +86,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
+import nodomain.freeyourgadget.gadgetbridge.externalevents.OpenTracksContentObserver;
+import nodomain.freeyourgadget.gadgetbridge.externalevents.OpenTracksController;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
@@ -508,6 +513,72 @@ public class DebugActivity extends AbstractGBActivity {
}
});
+ Button startFitnessAppTracking = findViewById(R.id.startFitnessAppTracking);
+ startFitnessAppTracking.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ OpenTracksController.startRecording(DebugActivity.this);
+ }
+ });
+
+ Button stopFitnessAppTracking = findViewById(R.id.stopFitnessAppTracking);
+ stopFitnessAppTracking.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ OpenTracksController.stopRecording(DebugActivity.this);
+ }
+ });
+
+ Button showStatusFitnessAppTracking = findViewById(R.id.showStatusFitnessAppTracking);
+ final int delay = 2 * 1000;
+
+ showStatusFitnessAppTracking.setOnClickListener(new View.OnClickListener() {
+ final Handler handler = new Handler();
+ Runnable runnable;
+
+ @Override
+ public void onClick(View v) {
+ final AlertDialog.Builder fitnesStatusBuilder = new AlertDialog.Builder(DebugActivity.this);
+ fitnesStatusBuilder
+ .setCancelable(false)
+ .setTitle("openTracksObserver Status")
+ .setMessage("Starting openTracksObserver watcher, waiting for an update, refreshing every: " + delay + "ms")
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ handler.removeCallbacks(runnable);
+ }
+ });
+ final AlertDialog alert = fitnesStatusBuilder.show();
+
+
+ runnable = new Runnable() {
+ @Override
+ public void run() {
+ LOG.debug("openTracksObserver debug watch dialog running");
+ handler.postDelayed(this, delay); //schedule next execution
+
+ OpenTracksContentObserver openTracksObserver = GBApplication.app().getOpenTracksObserver();
+ if (openTracksObserver == null) {
+ LOG.debug("openTracksObserver is null");
+ alert.cancel();
+ alert.setMessage("openTracksObserver not running");
+ alert.show();
+ return;
+ }
+ LOG.debug("openTracksObserver is not null, updating debug view");
+ long timeSecs = openTracksObserver.getTimeMillisChange() / 1000;
+ float distanceCM = openTracksObserver.getDistanceMeterChange() * 100;
+
+ LOG.debug("Time: " + timeSecs + " distanceCM " + distanceCM);
+ alert.cancel();
+ alert.setMessage("TimeSec: " + timeSecs + " distanceCM " + distanceCM);
+ alert.show();
+ }
+ };
+ handler.postDelayed(runnable, delay);
+ }
+ });
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationManagementActivity.java
index a007fb5d2..a585eb0f9 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationManagementActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationManagementActivity.java
@@ -42,13 +42,13 @@ import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
+import android.provider.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
-import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@@ -108,11 +108,31 @@ public class NotificationManagementActivity extends AbstractSettingsActivity {
category.removePreference(pref);
}
+ pref = findPreference("notifications_settings");
+ pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
+ //This could open notification channel settings, if needed...:
+ //Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
+ //intent.putExtra(Settings.EXTRA_CHANNEL_ID, GB.NOTIFICATION_CHANNEL_ID_TRANSFER);
+ startActivity(intent);
+ return true;
+ }
+ });
+
if (GBApplication.isRunningTenOrLater()) {
pref = findPreference("minimize_priority");
PreferenceCategory category = (PreferenceCategory) findPreference("pref_key_notifications");
category.removePreference(pref);
}
+
+ if (!GBApplication.isRunningOreoOrLater()) {
+ pref = findPreference("notifications_settings");
+ PreferenceCategory category = (PreferenceCategory) findPreference("pref_key_notifications");
+ category.removePreference(pref);
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
index b8a590671..5970cc691 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
@@ -19,8 +19,11 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.Manifest;
+import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
@@ -28,16 +31,19 @@ import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
-import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
@@ -73,7 +79,8 @@ public class SettingsActivity extends AbstractSettingsActivity {
public static final String PREF_MEASUREMENT_SYSTEM = "measurement_system";
private static final int FILE_REQUEST_CODE = 4711;
-
+ private EditText fitnessAppEditText = null;
+ private int fitnessAppSelectionListSpinnerFirstRun = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -306,7 +313,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
Integer.valueOf((String) autoExportInterval));
preference.setSummary(summary);
boolean auto_export_enabled = GBApplication.getPrefs().getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false);
- PeriodicExporter.sheduleAlarm(getApplicationContext(), Integer.valueOf((String) autoExportInterval), auto_export_enabled);
+ PeriodicExporter.scheduleAlarm(getApplicationContext(), Integer.valueOf((String) autoExportInterval), auto_export_enabled);
return true;
}
});
@@ -320,7 +327,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
@Override
public boolean onPreferenceChange(Preference preference, Object autoExportEnabled) {
int autoExportInterval = GBApplication.getPrefs().getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0);
- PeriodicExporter.sheduleAlarm(getApplicationContext(), autoExportInterval, (boolean) autoExportEnabled);
+ PeriodicExporter.scheduleAlarm(getApplicationContext(), autoExportInterval, (boolean) autoExportEnabled);
return true;
}
});
@@ -403,6 +410,70 @@ public class SettingsActivity extends AbstractSettingsActivity {
}
});
+ //fitness app (OpenTracks) package name selection for OpenTracks observer
+ pref = findPreference("pref_key_opentracks_packagename");
+ pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+
+ final LinearLayout outerLayout = new LinearLayout(SettingsActivity.this);
+ outerLayout.setOrientation(LinearLayout.VERTICAL);
+ final LinearLayout innerLayout = new LinearLayout(SettingsActivity.this);
+ innerLayout.setOrientation(LinearLayout.HORIZONTAL);
+ innerLayout.setPadding(20, 0, 20, 0);
+ final Spinner selectionListSpinner = new Spinner(SettingsActivity.this);
+ String[] appListArray = getResources().getStringArray(R.array.fitness_tracking_apps_package_names);
+ ArrayAdapter spinnerArrayAdapter = new ArrayAdapter(SettingsActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, appListArray);
+ selectionListSpinner.setAdapter(spinnerArrayAdapter);
+ fitnessAppSelectionListSpinnerFirstRun = 0;
+ addListenerOnSpinnerDeviceSelection(selectionListSpinner);
+ Prefs prefs = GBApplication.getPrefs();
+ String packageName = prefs.getString("opentracks_packagename", "de.dennisguse.opentracks");
+ fitnessAppEditText = new EditText(SettingsActivity.this);
+ fitnessAppEditText.setText(packageName);
+ innerLayout.addView(fitnessAppEditText);
+ outerLayout.addView(selectionListSpinner);
+ outerLayout.addView(innerLayout);
+
+ new AlertDialog.Builder(SettingsActivity.this)
+ .setCancelable(true)
+ .setTitle(R.string.pref_title_opentracks_packagename)
+ .setView(outerLayout)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
+ editor.putString("opentracks_packagename", fitnessAppEditText.getText().toString());
+ editor.apply();
+ editor.commit();
+ }
+ })
+ .setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ }
+ })
+ .show();
+ return false;
+ }
+ });
+ }
+
+ private void addListenerOnSpinnerDeviceSelection(Spinner spinner) {
+ spinner.setOnItemSelectedListener(new SettingsActivity.CustomOnDeviceSelectedListener());
+ }
+
+ public class CustomOnDeviceSelectedListener implements AdapterView.OnItemSelectedListener {
+ public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
+ if (++fitnessAppSelectionListSpinnerFirstRun > 1) { //this prevents the setText to be set when spinner just is being initialized
+ fitnessAppEditText.setText(parent.getItemAtPosition(pos).toString());
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // TODO Auto-generated method stub
+ }
}
@Override
@@ -421,7 +492,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
.getPrefs().getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false);
int autoExportPeriod = GBApplication
.getPrefs().getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0);
- PeriodicExporter.sheduleAlarm(getApplicationContext(), autoExportPeriod, autoExportEnabled);
+ PeriodicExporter.scheduleAlarm(getApplicationContext(), autoExportPeriod, autoExportEnabled);
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java
index 2fe98665e..e3d5e3568 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java
@@ -44,24 +44,30 @@ public class PeriodicExporter extends BroadcastReceiver {
public static void enablePeriodicExport(Context context) {
Prefs prefs = GBApplication.getPrefs();
+ GBApplication gbApp = GBApplication.app();
+ long autoExportScheduled = gbApp.getAutoExportScheduledTimestamp();
boolean autoExportEnabled = prefs.getBoolean(GBPrefs.AUTO_EXPORT_ENABLED, false);
Integer autoExportInterval = prefs.getInt(GBPrefs.AUTO_EXPORT_INTERVAL, 0);
- sheduleAlarm(context, autoExportInterval, autoExportEnabled);
+ scheduleAlarm(context, autoExportInterval, autoExportEnabled && autoExportScheduled == 0);
}
- public static void sheduleAlarm(Context context, Integer autoExportInterval, boolean autoExportEnabled) {
+ public static void scheduleAlarm(Context context, Integer autoExportInterval, boolean autoExportEnabled) {
Intent i = new Intent(context, PeriodicExporter.class);
- PendingIntent pi = PendingIntent.getBroadcast(context, 0 , i, 0);
+ PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);
if (!autoExportEnabled) {
+ LOG.info("Not scheduling periodic export, either already scheduled or not enabled");
return;
}
int exportPeriod = autoExportInterval * 60 * 60 * 1000;
if (exportPeriod == 0) {
+ LOG.info("Not scheduling periodic export, interval set to 0");
return;
}
- LOG.info("Enabling periodic export");
+ LOG.info("Scheduling periodic export");
+ GBApplication gbApp = GBApplication.app();
+ gbApp.setAutoExportScheduledTimestamp(System.currentTimeMillis() + exportPeriod);
am.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + exportPeriod,
@@ -72,21 +78,46 @@ public class PeriodicExporter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- LOG.info("Exporting DB");
- try (DBHandler dbHandler = GBApplication.acquireDB()) {
- DBHelper helper = new DBHelper(context);
- String dst = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null);
- if (dst == null) {
- LOG.info("Unable to export DB, export location not set");
- return;
+ LOG.info("Received command to export DB");
+ createRefreshTask("Export database", context).execute();
+ }
+
+ protected RefreshTask createRefreshTask(String task, Context context) {
+ return new RefreshTask(task, context);
+ }
+
+ public class RefreshTask extends DBAccess {
+ Context localContext;
+
+ public RefreshTask(String task, Context context) {
+ super(task, context);
+ localContext = context;
+ }
+
+ @Override
+ protected void doInBackground(DBHandler handler) {
+ LOG.info("Exporting DB in a background thread");
+ try (DBHandler dbHandler = GBApplication.acquireDB()) {
+ DBHelper helper = new DBHelper(localContext);
+ String dst = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null);
+ if (dst == null) {
+ LOG.info("Unable to export DB, export location not set");
+ return;
+ }
+ Uri dstUri = Uri.parse(dst);
+ try (OutputStream out = localContext.getContentResolver().openOutputStream(dstUri)) {
+ helper.exportDB(dbHandler, out);
+ GBApplication gbApp = GBApplication.app();
+ gbApp.setLastAutoExportTimestamp(System.currentTimeMillis());
+ }
+ } catch (Exception ex) {
+ GB.updateExportFailedNotification(localContext.getString(R.string.notif_export_failed_title), localContext);
+ LOG.info("Exception while exporting DB: ", ex);
}
- Uri dstUri = Uri.parse(dst);
- try (OutputStream out = context.getContentResolver().openOutputStream(dstUri)) {
- helper.exportDB(dbHandler, out);
- }
- } catch (Exception ex) {
- GB.updateExportFailedNotification(context.getString(R.string.notif_export_failed_title), context);
- LOG.info("Exception while exporting DB: ", ex);
+ }
+
+ @Override
+ protected void onPostExecute(Object o) {
}
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/fitpro/FitProDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/fitpro/FitProDeviceCoordinator.java
index 8195c76e5..3f9a0911a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/fitpro/FitProDeviceCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/fitpro/FitProDeviceCoordinator.java
@@ -63,7 +63,8 @@ public class FitProDeviceCoordinator extends AbstractDeviceCoordinator {
if (name != null && (
name.startsWith("M6") ||
name.startsWith("M4") ||
- name.equals("LH716"))
+ name.equals("LH716") ||
+ name.equals("Fit1900"))
) {
return DeviceType.FITPRO;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java
index d57ae3816..d9cdc70b0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java
@@ -80,6 +80,8 @@ public class HuamiConst {
public static final String PREF_BUTTON_ACTION_BROADCAST = "button_action_broadcast";
public static final String PREF_BUTTON_ACTION_SELECTION_OFF = "UNKNOWN";
public static final String PREF_BUTTON_ACTION_SELECTION_BROADCAST = "BROADCAST";
+ public static final String PREF_BUTTON_ACTION_SELECTION_FITNESS_APP_START = "FITNESS_CONTROL_START";
+ public static final String PREF_BUTTON_ACTION_SELECTION_FITNESS_APP_STOP = "FITNESS_CONTROL_STOP";
public static final String PREF_DEVICE_ACTION_SELECTION_OFF = "UNKNOWN";
public static final String PREF_DEVICE_ACTION_SELECTION_BROADCAST = "BROADCAST";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilAppWriter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilAppWriter.java
index fc8b199c7..4917e879a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilAppWriter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilAppWriter.java
@@ -39,11 +39,11 @@ public class FossilAppWriter {
private String version;
private LinkedHashMap code;
private LinkedHashMap icons;
- private LinkedHashMap layout;
+ private LinkedHashMap layout;
private LinkedHashMap displayName;
private LinkedHashMap config;
- public FossilAppWriter(Context context, String version, LinkedHashMap code, LinkedHashMap icons, LinkedHashMap layout, LinkedHashMap displayName, LinkedHashMap config) {
+ public FossilAppWriter(Context context, String version, LinkedHashMap code, LinkedHashMap icons, LinkedHashMap layout, LinkedHashMap displayName, LinkedHashMap config) {
this.mContext = context;
if (this.mContext == null) throw new AssertionError("context cannot be null");
this.version = version;
@@ -61,9 +61,9 @@ public class FossilAppWriter {
}
public byte[] getWapp() throws IOException {
- byte[] codeData = loadFiles(code);
- byte[] iconsData = loadFiles(icons);
- byte[] layoutData = loadStringFiles(layout);
+ byte[] codeData = loadFiles(code, false);
+ byte[] iconsData = loadFiles(icons, false);
+ byte[] layoutData = loadFiles(layout, true);
byte[] displayNameData = loadStringFiles(displayName);
byte[] configData = loadStringFiles(config);
@@ -118,16 +118,23 @@ public class FossilAppWriter {
return wapp.toByteArray();
}
- public byte[] loadFiles(LinkedHashMap filesMap) throws IOException {
+ public byte[] loadFiles(LinkedHashMap filesMap, boolean appendNull) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
for (String filename : filesMap.keySet()) {
InputStream in = filesMap.get(filename);
output.write((byte)filename.length() + 1);
output.write(StringUtils.terminateNull(filename).getBytes(StandardCharsets.UTF_8));
- output.write(shortToLEBytes((short)in.available()));
+ int fileLength = in.available();
+ if(appendNull){
+ fileLength++;
+ }
+ output.write(shortToLEBytes((short)fileLength));
byte[] fileBytes = new byte[in.available()];
in.read(fileBytes);
output.write(fileBytes);
+ if(appendNull){
+ output.write(0x00);
+ }
}
return output.toByteArray();
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java
index 0a3b5cd87..3464ca3a5 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java
@@ -456,6 +456,9 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
if (watchfaceConfig.has("powersave_hands")) {
watchfaceSettings.setPowersaveHands(watchfaceConfig.getBoolean("powersave_hands"));
}
+ if (watchfaceConfig.has("light_up_on_notification")) {
+ watchfaceSettings.setLightUpOnNotification(watchfaceConfig.getBoolean("light_up_on_notification"));
+ }
} catch (JSONException e) {
LOG.warn("JSON parsing error", e);
}
@@ -576,39 +579,46 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
if (widget != null) {
posY.setText(Integer.toString(widget.getPosY()));
}
- // Configure position preset buttons
- Button btnTop = layout.findViewById(R.id.watchface_widget_preset_top);
- btnTop.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- posX.setText("120");
- posY.setText("58");
+
+ class WidgetPosition{
+ final int posX, posY, buttonResource, hintStringResource;
+
+ public WidgetPosition(int posX, int posY, int buttonResource, int hintStringResource) {
+ this.posX = posX;
+ this.posY = posY;
+ this.buttonResource = buttonResource;
+ this.hintStringResource = hintStringResource;
}
- });
- Button btnBottom = layout.findViewById(R.id.watchface_widget_preset_bottom);
- btnBottom.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- posX.setText("120");
- posY.setText("182");
+ }
+
+ WidgetPosition[] positions = new WidgetPosition[]{
+ new WidgetPosition(120, 58, R.id.watchface_widget_preset_top, R.string.watchface_dialog_widget_preset_top),
+ new WidgetPosition(182, 120, R.id.watchface_widget_preset_right, R.string.watchface_dialog_widget_preset_right),
+ new WidgetPosition(120, 182, R.id.watchface_widget_preset_bottom, R.string.watchface_dialog_widget_preset_bottom),
+ new WidgetPosition(58, 120, R.id.watchface_widget_preset_left, R.string.watchface_dialog_widget_preset_left),
+ };
+
+ for(final WidgetPosition position : positions){
+ Button btn = layout.findViewById(position.buttonResource);
+ btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ posX.setText(String.valueOf(position.posX));
+ posY.setText(String.valueOf(position.posY));
+ }
+ });
+ }
+
+ if(widget == null){
+ int currentIndex = widgets.size();
+ if(currentIndex < 4){
+ WidgetPosition newPosition = positions[currentIndex];
+ posX.setText(String.valueOf(newPosition.posX));
+ posY.setText(String.valueOf(newPosition.posY));
+ GB.toast(getString(R.string.watchface_dialog_pre_setting_position, getString(newPosition.hintStringResource)), Toast.LENGTH_SHORT, GB.INFO);
}
- });
- Button btnLeft = layout.findViewById(R.id.watchface_widget_preset_left);
- btnLeft.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- posX.setText("58");
- posY.setText("120");
- }
- });
- Button btnRight = layout.findViewById(R.id.watchface_widget_preset_right);
- btnRight.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- posX.setText("182");
- posY.setText("120");
- }
- });
+ }
+
// Set widget size
final LinearLayout sizeLayout = layout.findViewById(R.id.watchface_widget_size_layout);
sizeLayout.setVisibility(View.GONE);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java
index b0cdfd227..be473880a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java
@@ -185,22 +185,15 @@ public class HybridHRWatchfaceFactory {
} catch (IOException e) {
LOG.warn("Unable to read asset file", e);
}
- LinkedHashMap layout = new LinkedHashMap<>();
- try {
- layout.put("complication_layout", getComplicationLayout());
- } catch (JSONException e) {
- LOG.warn("Could not generate complication_layout", e);
- }
- try {
- layout.put("image_layout", getImageLayout());
- } catch (JSONException e) {
- LOG.warn("Could not generate image_layout", e);
- }
- try {
- if (includeWidget("widgetBattery") > 0) layout.put("battery_layout", getBatteryLayout());
- } catch (JSONException e) {
- LOG.warn("Could not generate battery_layout", e);
+ LinkedHashMap layout = new LinkedHashMap<>();
+ layout.put("complication_layout", context.getAssets().open("fossil_hr/complication_layout.json"));
+ layout.put("image_layout", context.getAssets().open("fossil_hr/image_layout.json"));
+ layout.put("menu_layout", context.getAssets().open("fossil_hr/menu_layout.json"));
+
+ if (includeWidget("widgetBattery") > 0) {
+ layout.put("battery_layout", context.getAssets().open("fossil_hr/battery_layout.json"));
}
+
LinkedHashMap displayName = new LinkedHashMap<>();
displayName.put("display_name", watchfaceName);
displayName.put("theme_class", "complications");
@@ -214,184 +207,6 @@ public class HybridHRWatchfaceFactory {
return appWriter.getWapp();
}
- private String getBatteryLayout() throws JSONException {
- JSONArray batteryLayout = new JSONArray();
-
- JSONObject complicationBackground = new JSONObject();
- complicationBackground.put("id", 0);
- complicationBackground.put("type", "complication_background");
- complicationBackground.put("background", "#background");
- complicationBackground.put("visible", true);
- complicationBackground.put("inversion", false);
- JSONObject goalRing = new JSONObject();
- goalRing.put("is_enable", "#goal_ring");
- goalRing.put("end_angle", "#fi");
- goalRing.put("is_invert", "#$e");
- complicationBackground.put("goal_ring", goalRing);
- JSONObject dimension = new JSONObject();
- dimension.put("type", "rigid");
- dimension.put("width", "#size.w");
- dimension.put("height", "#size.h");
- complicationBackground.put("dimension", dimension);
- JSONObject placement = new JSONObject();
- placement.put("type", "absolute");
- placement.put("left", "#pos.Ue");
- placement.put("top", "#pos.Qe");
- complicationBackground.put("placement", placement);
- batteryLayout.put(complicationBackground);
-
- JSONObject complicationContent = new JSONObject();
- complicationContent.put("id", 1);
- complicationContent.put("parent_id", 0);
- complicationContent.put("type", "complication_content");
- complicationContent.put("icon", "icBattery");
- complicationContent.put("text_low", "#ci");
- complicationContent.put("visible", true);
- complicationContent.put("inversion", "#$e");
- dimension = new JSONObject();
- dimension.put("type", "rigid");
- dimension.put("width", 76);
- dimension.put("height", 76);
- complicationContent.put("dimension", dimension);
- placement = new JSONObject();
- placement.put("type", "relative");
- complicationContent.put("placement", placement);
- batteryLayout.put(complicationContent);
-
- JSONObject chargingStatus = new JSONObject();
- chargingStatus.put("id", 2);
- chargingStatus.put("parent_id", 1);
- chargingStatus.put("type", "solid");
- chargingStatus.put("color", "#nt");
- chargingStatus.put("visible", true);
- chargingStatus.put("inversion", false);
- dimension = new JSONObject();
- dimension.put("type", "rigid");
- dimension.put("width", "#it");
- dimension.put("height", 6);
- chargingStatus.put("dimension", dimension);
- placement = new JSONObject();
- placement.put("type", "absolute");
- placement.put("left", 29);
- placement.put("top", 23);
- chargingStatus.put("placement", placement);
- batteryLayout.put(chargingStatus);
-
- JSONObject image = new JSONObject();
- image.put("id", 3);
- image.put("parent_id", 1);
- image.put("type", "image");
- image.put("image_name", "icBattCharging");
- image.put("draw_mode", 1);
- image.put("visible", "#et");
- image.put("inversion", false);
- placement = new JSONObject();
- placement.put("type", "absolute");
- placement.put("left", 34);
- placement.put("top", 21);
- image.put("placement", placement);
- dimension = new JSONObject();
- dimension.put("width", 6);
- dimension.put("height", 9);
- image.put("dimension", dimension);
- batteryLayout.put(image);
-
- return batteryLayout.toString();
- }
-
- private String getComplicationLayout() throws JSONException {
- JSONArray complicationLayout = new JSONArray();
-
- JSONObject complicationBackground = new JSONObject();
- complicationBackground.put("id", 0);
- complicationBackground.put("type", "complication_background");
- complicationBackground.put("background", "#background");
- complicationBackground.put("visible", true);
- complicationBackground.put("inversion", false);
- JSONObject goalRing = new JSONObject();
- goalRing.put("is_enable", "#goal_ring");
- goalRing.put("end_angle", "#fi");
- goalRing.put("is_invert", "#$e");
- complicationBackground.put("goal_ring", goalRing);
- JSONObject dimension = new JSONObject();
- dimension.put("type", "rigid");
- dimension.put("width", "#size.w");
- dimension.put("height", "#size.h");
- complicationBackground.put("dimension", dimension);
- JSONObject placement = new JSONObject();
- placement.put("type", "absolute");
- placement.put("left", "#pos.Ue");
- placement.put("top", "#pos.Qe");
- complicationBackground.put("placement", placement);
- complicationLayout.put(complicationBackground);
-
- JSONObject complicationContent = new JSONObject();
- complicationContent.put("id", 1);
- complicationContent.put("parent_id", 0);
- complicationContent.put("type", "complication_content");
- complicationContent.put("icon", "#icon");
- complicationContent.put("text_high", "#dt");
- complicationContent.put("text_low", "#ci");
- complicationContent.put("visible", true);
- complicationContent.put("inversion", "#$e");
- dimension = new JSONObject();
- dimension.put("type", "rigid");
- dimension.put("width", "#size.w");
- dimension.put("height", "#size.h");
- complicationContent.put("dimension", dimension);
- placement = new JSONObject();
- placement.put("type", "relative");
- complicationContent.put("placement", placement);
- complicationLayout.put(complicationContent);
-
- return complicationLayout.toString();
- }
-
- private String getImageLayout() throws JSONException {
- JSONArray imageLayout = new JSONArray();
-
- JSONObject container = new JSONObject();
- container.put("id", 0);
- container.put("type", "container");
- container.put("direction", 1);
- container.put("main_alignment", 1);
- container.put("cross_alignment", 1);
- container.put("visible", true);
- container.put("inversion", false);
- JSONObject dimension = new JSONObject();
- dimension.put("type", "rigid");
- dimension.put("width", 240);
- dimension.put("height", 240);
- container.put("dimension", dimension);
- JSONObject placement = new JSONObject();
- placement.put("type", "absolute");
- placement.put("left", 0);
- placement.put("top", 0);
- container.put("placement", placement);
- imageLayout.put(container);
-
- JSONObject image = new JSONObject();
- image.put("id", 1);
- image.put("parent_id", 0);
- image.put("type", "image");
- image.put("image_name", "#name");
- image.put("draw_mode", 1);
- image.put("visible", true);
- image.put("inversion", false);
- placement = new JSONObject();
- placement.put("type", "absolute");
- placement.put("left", "#pos.Ue");
- placement.put("top", "#pos.Qe");
- image.put("placement", placement);
- dimension = new JSONObject();
- dimension.put("width", "#size.w");
- dimension.put("height", "#size.h");
- image.put("dimension", dimension);
- imageLayout.put(image);
-
- return imageLayout.toString();
- }
-
private String getConfiguration() throws JSONException {
JSONObject configuration = new JSONObject();
@@ -432,6 +247,7 @@ public class HybridHRWatchfaceFactory {
config.put("wrist_flick_duration", settings.getWristFlickDuration());
config.put("wrist_flick_move_hour", settings.getWristFlickMoveHour());
config.put("wrist_flick_move_minute", settings.getWristFlickMoveMinute());
+ config.put("light_up_on_notification", settings.getLightUpOnNotification());
config.put("powersave_display", settings.getPowersaveDisplay());
config.put("powersave_hands", settings.getPowersaveHands());
configuration.put("config", config);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettings.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettings.java
index cd402da3e..ef3ebe074 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettings.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettings.java
@@ -27,6 +27,7 @@ public class HybridHRWatchfaceSettings implements Serializable {
private int wristFlickMoveMinute = -360;
private boolean powersaveDisplay = false;
private boolean powersaveHands = false;
+ private boolean lightUpOnNotification = false;
public HybridHRWatchfaceSettings() {
}
@@ -63,6 +64,14 @@ public class HybridHRWatchfaceSettings implements Serializable {
this.wristFlickDuration = wristFlickDuration;
}
+ public boolean getLightUpOnNotification() {
+ return lightUpOnNotification;
+ }
+
+ public void setLightUpOnNotification(boolean lightUpOnNotification) {
+ this.lightUpOnNotification = lightUpOnNotification;
+ }
+
public int getWristFlickMoveHour() {
return wristFlickMoveHour;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettingsActivity.java
index 2fb1a9725..50485eb99 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettingsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceSettingsActivity.java
@@ -111,6 +111,10 @@ public class HybridHRWatchfaceSettingsActivity extends AbstractSettingsActivity
SwitchPreference power_saving_hands = (SwitchPreference) findPreference("pref_hybridhr_watchface_power_saving_hands");
power_saving_hands.setOnPreferenceChangeListener(new PreferenceChangeListener());
power_saving_hands.setChecked(settings.getPowersaveHands());
+
+ SwitchPreference light_up_on_notification = (SwitchPreference) findPreference("pref_hybridhr_watchface_light_up_on_notification");
+ light_up_on_notification.setOnPreferenceChangeListener(new PreferenceChangeListener());
+ light_up_on_notification.setChecked(settings.getLightUpOnNotification());
}
private static class PreferenceChangeListener implements Preference.OnPreferenceChangeListener {
@@ -136,6 +140,9 @@ public class HybridHRWatchfaceSettingsActivity extends AbstractSettingsActivity
settings.setWristFlickMoveMinute(Integer.parseInt(newValue.toString()));
preference.setSummary(newValue.toString());
break;
+ case "pref_hybridhr_watchface_light_up_on_notification":
+ settings.setLightUpOnNotification((boolean) newValue);
+ break;
case "pref_hybridhr_watchface_wrist_flick_duration":
settings.setWristFlickDuration(Integer.parseInt(newValue.toString()));
preference.setSummary(newValue.toString());
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java
index 1b4cee1ed..485398cdf 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java
@@ -5,17 +5,21 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
+import android.view.View;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.lang.reflect.Field;
import java.util.HashMap;
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Data.MeasurementData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Support;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class DataActivity extends AbstractGBActivity {
private HashMap valueViews = new HashMap<>(ValueDisplay.values().length);
@@ -51,6 +55,21 @@ public class DataActivity extends AbstractGBActivity {
setContentView(R.layout.activity_um25_data);
chargeDurationTextView = findViewById(R.id.um25_text_charge_duration);
+ TextView wattHoursTextView = findViewById(R.id.um25_text_wattage_sum);
+
+ View.OnLongClickListener longClickListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ GB.toast("resetting", Toast.LENGTH_SHORT, GB.INFO);
+ LocalBroadcastManager.getInstance(DataActivity.this).sendBroadcast(
+ new Intent(UM25Support.ACTION_RESET_STATS)
+ );
+ return true;
+ }
+ };
+
+ chargeDurationTextView.setOnLongClickListener(longClickListener);
+ wattHoursTextView.setOnLongClickListener(longClickListener);
}
@Override
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java
index d4d9de44f..8c745f975 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java
@@ -72,8 +72,8 @@ public class VescControlActivity extends AbstractGBActivity {
}
private void restoreValues(){
- rpmEditText.setText(preferences.getInt(PREFS_KEY_LAST_RPM, 0));
- breakCurrentEditText.setText(preferences.getInt(PREFS_KEY_LAST_BREAK_CURRENT, 0));
+ rpmEditText.setText(String.valueOf(preferences.getInt(PREFS_KEY_LAST_RPM, 0)));
+ breakCurrentEditText.setText(String.valueOf(preferences.getInt(PREFS_KEY_LAST_BREAK_CURRENT, 0)));
}
@Override
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java
index 40c298175..d7a80c589 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java
@@ -29,6 +29,9 @@ public class XWatchService {
public static final byte COMMAND_ACTION_BUTTON = 0x4c;
public static final byte COMMAND_ACTIVITY_DATA = 0x43;
public static final byte COMMAND_ACTIVITY_TOTALS = 0x46;
+ public static final byte COMMAND_NOTIFICATION = 0x4d;
+ public static final byte COMMAND_NOTIFICATION_PHONE = 0x00;
+ public static final byte COMMAND_NOTIFICATION_MESSAGE = 0x01;
private static final Map XWATCH_DEBUG;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java
index 208b90b0f..f5e916486 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java
@@ -38,7 +38,7 @@ public class AutoStartReceiver extends BroadcastReceiver {
} else {
GBApplication.deviceService().start();
}
-
+ Log.i(TAG, "Going to enable periodic exporter");
PeriodicExporter.enablePeriodicExport(context);
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java
index 3bab231c7..1402a92d1 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java
@@ -881,6 +881,7 @@ public class NotificationListener extends NotificationListenerService {
String source = sbn.getPackageName();
if (source.equals("de.dennisguse.opentracks")
|| source.equals("de.dennisguse.opentracks.debug")
+ || source.equals("de.dennisguse.opentracks.nightly")
|| source.equals("de.tadris.fitness")
|| source.equals("de.tadris.fitness.debug")
) {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OpenTracksContentObserver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OpenTracksContentObserver.java
new file mode 100644
index 000000000..aee21b9da
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OpenTracksContentObserver.java
@@ -0,0 +1,393 @@
+/* Copyright (C) 2022 Arjan Schrijver
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+
+package nodomain.freeyourgadget.gadgetbridge.externalevents;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class OpenTracksContentObserver extends ContentObserver {
+ private Context mContext;
+ private Uri tracksUri;
+ private int protocolVersion;
+ private int totalTimeMillis;
+ private float totalDistanceMeter;
+
+ private long previousTimeMillis = 0;
+ private float previousDistanceMeter = 0;
+
+ public int getTotalTimeMillis() {
+ return totalTimeMillis;
+ }
+ public float getTotalDistanceMeter() {
+ return totalDistanceMeter;
+ }
+
+ public long getTimeMillisChange() {
+ /**
+ * We don't use the timeMillis received from OpenTracks here, because those updates do not
+ * come in very regularly when GPS reception is bad
+ */
+ long timeMillisDelta = System.currentTimeMillis() - previousTimeMillis;
+ previousTimeMillis = System.currentTimeMillis();
+ return timeMillisDelta;
+ }
+
+ public float getDistanceMeterChange() {
+ float distanceMeterDelta = totalDistanceMeter - previousDistanceMeter;
+ previousDistanceMeter = totalDistanceMeter;
+ return distanceMeterDelta;
+ }
+
+
+ public OpenTracksContentObserver(Context context, final Uri tracksUri, final int protocolVersion) {
+ super(new Handler());
+ this.mContext = context;
+ this.tracksUri = tracksUri;
+ this.protocolVersion = protocolVersion;
+ this.previousTimeMillis = System.currentTimeMillis();
+ }
+
+ @Override
+ public void onChange(final boolean selfChange, final Uri uri) {
+ if (uri == null) {
+ return; // nothing can be done without an uri
+ }
+ if (tracksUri.toString().startsWith(uri.toString())) {
+ final List
Anzahl der Tastendrücke
Streichen nach links/rechts im Diagrammbetrieb aktivieren
- Wenn aktiviert, kann Zifferblatt Wetter, Akkuinfos, usw. anzeigen
+ Wenn aktiviert, kann Zifferblatt Wetter, Akkuinfos, usw. anzeigen.
Ganztägige Messung der Herzfrequenz
einmal pro Minute
alle 5 Minuten
@@ -1589,4 +1589,8 @@
Verwende den Kopfhörer wie gewohnt. Wenn sich die Tragebedingungen oder der Luftdruck ändern, führe den Optimierer erneut aus.
Amazfit Pop
Amazfit Pop Pro
-
\ No newline at end of file
+ OpenTracks Paketname
+ Dient zum Starten/Stoppen der GPS-Track-Aufzeichnung in der externen Fitness-App.
+ Android-Benachrichtigungseinstellungen
+ setze position auf %s
+
diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml
index 95d9e04f3..401143723 100644
--- a/app/src/main/res/values-en-rGB/strings.xml
+++ b/app/src/main/res/values-en-rGB/strings.xml
@@ -1173,7 +1173,7 @@
\nNote: You do not have to install .res if it is exactly the same as the one previously installed.
\n
\nPROCEED AT YOUR OWN RISK!
- Jump Rope
+ Skipping rope
Do not turn off
400
Save and apply
@@ -1578,4 +1578,16 @@
Use the headphones as you normally would. If the wearing condition or atmospheric pressure change, run the optimiser again.
Amazfit Pop Pro
Amazfit Pop
+ Nothing
+ Last AutoExport: %1$s
+ AutoExport has not been not scheduled.
+ Android notification settings
+ AutoExport has (originally) been scheduled for %1$s
+ Location could not be understood. Likely an issue of newer Android permission system. Most likely, autoexport is not working now.
+ OpenTracks package name
+ Used for starting/stopping GPS track recording in external fitness app.
+ AutoExport is enabled.
+ AutoExport is not enabled.
+ Fitness App Tracking Start
+ Fitness App Tracking Stop
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 6141e35d4..3cba4fce6 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1585,4 +1585,10 @@
Códec de audio
Información del dispositivo
Pulse para iniciar el optimizador de cancelación de ruido.
+ Nada
+ Ajustes de notificaciones de Android
+ Inicio del seguimiento de la aplicación de ejercicio físico
+ Parada de seguimiento de la aplicación de ejercicio físico
+ Nombre del paquete OpenTracks
+ Se usa para iniciar/detener la grabación del seguimiento GPS en la aplicación externa de ejercicio físico.
\ No newline at end of file
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 3334d9e88..f2c7af838 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -434,4 +434,44 @@
Akku vähissä
Odottaa yhteyden muodostumista
Tietoja sinusta
+ Aktiiviset askeleet
+ Navigaatio
+ Askeleita minuutissa
+ Linkit
+ Maanantai
+ Keskiviikko
+ Sunnuntai
+ Laitekohtaiset asetukset
+ Kalorit
+ Etäisyys
+ Askeleiden historia
+ Kokonaisaskeleet
+ Tietoja
+ Tietoja Gadgetbridgestä
+ Alkaen
+ Päättyen
+ Vastaamatta jääneen puhelun ilmoitus
+ Versio %s
+ Askeleet
+ Sähköposti-ilmoitus
+ Sosiaalinen verkosto
+ Saapuvan puhelun ilmoitus
+ Torstai
+ Tiistai
+ Kalenteri-ilmoitus
+ Kesto
+ Kesto (ms):
+ Alkaen %1$s päättyen %2$s
+ Askeleita viikossa
+ Laitekohtaiset asetukset
+ Lauantai
+ Perjantai
+ Leveysaste
+ Pituusaste
+ ###ft
+ ###.#km
+ ###m
+ VESC
+ Leveys:
+ Aikavyöhyke:
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index b3c933620..6e3c5c682 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -914,7 +914,7 @@ Temps de sommeil préféré en heures
Au sommeil
Actions de l\'appareil
Utiliser les évènements de l\'appareil pour déclencher des actions et des messages diffusé Android
- Période
+ Suivi des Cycles féminins
Style libre
Rythme mammaire
aujourd\'hui
@@ -1583,4 +1583,17 @@ Temps de sommeil préféré en heures
Mesure des conditions actuelles…
Amazfit Pop
Amazfit Pop Pro
+ Rien
+ Nom du logiciel OpenTracks
+ Utilisé pour démarrer/arrêter l\'enregistrement GPS dans une app. de fitness externe.
+ Démarrage de l\'enregistrement dans l\'app Fitness
+ Arrêt de l\'enregistrement dans l\'app Fitness
+ Configuration des notifications Android
+ Dernier export automatique : %1$s
+ L\'export automatique est activé.
+ L\'export automatique a été (initialement) réglé pour %1$s
+ L\'export automatique n\'a pas été planifié.
+ L\'export automatique est désactivé.
+ L\'emplacement n\'a pas pu être détecté. Probablement un problème avec les nouvelles permissions du système Android. Très probablement, l\'export automatique ne fonctionne pas actuellement.
+ pré-configuration de la position à %s
\ No newline at end of file
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 479d00e44..9fe7ad229 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -931,7 +931,7 @@
\n
\nהמשך מעבר לנקודה זו הוא על אחריותך!
לחץ
- מעקב מחזוריות
+ מעקב מחזור
נשימה
Mi Band 5
הפעלת אפשרות זו תתעלם ממכשירים שכבר אוגדו/צומדו טרם הסריקה
@@ -1579,4 +1579,17 @@
ניתן להשתמש באוזניות כרגיל. אם מצב הבלאי או הלחץ האטמוספרי משתנה, יש להריץ את המשפר שוב.
Amazfit Pop
Amazfit Pop Pro
+ כלום
+ שם חבילה ב־OpenTracks
+ משמש להפעלת/עצירת הקלטת מסלולי GPS מיישומוני חיטוב חיצוניים.
+ עצירת מעקב יישומון חיטוב
+ התחלת מעקב יישומון חיטוב
+ הגדרות הודעות של Android
+ הייצוא האוטומטי האחרון: %1$s
+ ייצוא אוטומטי מופעל.
+ ייצוא אוטומטי מושבת.
+ הייצוא האוטומטי לא תוזמן.
+ הייצוא האוטומטי תוזמן (במקור) ל־%1$s
+ לא ניתן לפענח את המיקום. כנראה תקלה במערכת ההרשאות החדשה של Android. סביר להניח שהייצוא האוטומטי לא עובד כרגע.
+ המיקום מוגדר מראש לכדי %s
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 28a8dcc17..711a0edb3 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1552,4 +1552,9 @@
Speak-to-chat
Informacije urađaja
Audio kodek
+ Početak
+ Amazfit Pop
+ Amazfit Pop Pro
+ Ništa
+ Nepoznato
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index ee1259c06..f6ab2f1c9 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -121,7 +121,7 @@
Acquisisci posizione
Latitudine
Longitudine
- Mantieni aggiornata la posizion
+ Mantieni aggiornata la posizione
Cerca di ottenere la posizione aggiornata durante l\'utilizzo, usa quella memorizzata come backup
Per cortesia abilita la localizzazione utilizzando la rete
posizione acquisita
@@ -317,8 +317,8 @@
La band vibrerà una volta raggiunto l\'obbiettivo passi giornalieri
Elementi mostrati
Scegli gli elementi da mostrare sullo schermo della band
- Attiva il display quando sollevato
- Rotazione polso per cambiare vista
+ Attiva il display quando il dispositivo viene sollevato
+ Rotazione del polso per passare da una vista all\'altra
Non disturbare
La band non riceverà notifiche quando è attiva
Allarmi inattività
@@ -564,7 +564,7 @@
30 minuti
Hai dormito dalle %1$s alle %2$s
Non hai dormito
- Swipe up per sbloccare lo schermo della band
+ Scorri verso l\'alto per sbloccare lo schermo della band
Modalità notturna
Norvegese Bokmål
Russo
@@ -1120,8 +1120,8 @@
Silenzia cellulare
Trova cellulare
Ossigenazione sangue
- Mantieni visibile via Bluetooth anche quando connesso
- Visibile mentre connessi
+ Mantieni visibile il dispositivo via Bluetooth anche quando questo è connesso
+ Dispositivo visibile durante la connessione
Chiudi automaticamente le notifiche SMS
Suoneria continua fittizia
Suoni di operazione
@@ -1261,4 +1261,50 @@
Impostazioni sviluppatore
Aggiungi widget
Impostazioni incomplete, widget non aggiunto
+ Data
+ Sconosciuto
+ Configura promemoria
+ Ripeti
+ Orario
+ Messaggio
+ Promemoria da riservare per i prossimi eventi del calendario
+ Spegni
+ Spegni
+ Sei sicuro di voler spegnere il dispositivo\?
+ Informazioni sull\'attività nella carta del dispositivo
+ Scegli quale attività mostrare nella carta del dispositivo
+ Mostra informazioni sull\'attività nella carta del dispositivo
+ Mostra i passi, la distanza o il sonno attuali nella carta del dispositivo
+ Mostra la durata del sonno
+ La distanza viene calcolata dai passi e dalla lunghezza del passo (configurabile in Impostazioni - Informazioni sull\'utilizzatore)
+ Mostra passi totali
+ Sistema
+ Equalizzatore
+ Utilizza la lista delle applicazioni per...
+ Modalità privacy per i messaggi
+ Mostra contenuto
+ Configura promemoria
+ Spazio esaurito
+ Il dispositivo non ha spazio libero per aggiungere altri promemoria (totali: %1$s)
+ Dettagli promemoria
+ %1$s, ogni giorno
+ %1$s, ogni settimana
+ %1$s, ogni mese
+ %1$s, ogni anno
+ Una volta sola
+ Ogni giorno
+ Ogni settimana
+ Ogni mese
+ Ogni anno
+ Elimina promemoria
+ Sei sicuro di voler eliminare il promemoria\?
+ Attiva il display quando il dispositivo viene sollevato durante la modalità non disturbare
+ Suono di ping
+ Sonno
+ Altro
+ Scansione Bluetooth:
+ Scansione Bluetooth LE:
+ %1$s, una volta sola
+ Controlla tutte le applicazioni
+ Numero di eventi del calendario che verranno sincronizzati
\ No newline at end of file
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 15cc874e0..89b097f23 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -303,7 +303,7 @@
Aktivitet i sanntid
Steg i dag, mål: %1$s
Ikke send bekreftelse på aktivitetsdataoverføring
- Hvis aktivitetsdataen ikke bekreftes til bandet, vil de ikke bli tømt. Nyttig hvis Gb brukes sammen med andre programmer.
+ Hvis de ikke bekreftes til bandet, vil de ikke bli tømt. Nyttig hvis GB brukes sammen med andre programmer.
Beholder aktivitetsdata på Mi Band-et, selv etter synkronisering. Nyttig hvis Gb brukes sammen med andre programmer.
Bruk lav-latens-modus for fastvareoppgraderinger
Dette kan hjelpe på enheter der fastvareoppgraderinger mislykkes.
@@ -377,7 +377,7 @@
Feil ved eksport av DB: %1$s
Feil ved eksport av innstilling: %1$s
Importer data?
- Vil du virkelig overskrive gjeldende database? All nåværende aktivitetsdata (hvis noen) vil gå tapt.
+ Vil du virkelig overskrive gjeldende data\? All nåværende aktivitetsdata (hvis noen) vil bli overskrevet.
Importert.
Feil ved import av DB: %1$s
Feil ved import av innstilling: %1$s
@@ -906,7 +906,7 @@
Om
Verdensklokke
Stress
- Periode
+ Følg m.-syklus
Pusting
Mi Band 5
Stigende
@@ -1096,7 +1096,7 @@
Varsle om tapt anrop
Merknad for oppringing
Merknad for tapt anrop
- juster visere til 12:00
+ Bruk knappene nedenfor for å justere viserne til 12:00.
GPS-spor
Versjon %s
Nedre knapp dobbel
@@ -1579,4 +1579,20 @@
Bruk hodetelefonene som vanlig. Hvis måten du bruker dem på eller lufttrykket endrer seg kan du kjøre optimalisatoren igjen.
Enhetsinfo
Optimaliser
+ Ingenting
+ Amazfit Pop
+ Amazfit Pop Pro
+ Sony WH-1000XM4
+ Sporingsstart for trimprogram
+ Sporingsstopp for trimprogram
+ OpenTracks-pakkenavn
+ Android-merknadsinnstillinger
+ Brukt for start/stopp av GPS-sporingslagring i eksternt trimprogram.
+ Auto-eksport er påslått.
+ Auto-eksport er avslått.
+ Siste auto-eksport:%1$s
+ Auto-eksport ble (opprinnelig) planlagt %1$s
+ Auto-eksport er ikke planlagt.
+ Forsto ikke plassering. Antagelig er dette et tilgangsproblem i Android. Mest sannsynlig fungerer ikke auto-eksport nå.
+ forhåndsinnstiller posisjon til %s
\ No newline at end of file
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 4ec82bc14..7acb91769 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -1580,4 +1580,16 @@
Starten…
Amazfit Pop Pro
Amazfit Pop
+ Niets
+ Wordt gebruikt voor het starten/stoppen van GPS-trackregistratie in externe fitness-app.
+ OpenTracks-pakketnaam
+ Fitness-app-tracking starten
+ Fitness-app-tracking stoppen
+ Instellingen voor Android-meldingen
+ AutoExport is niet gepland.
+ Locatie was niet te begrijpen. Waarschijnlijk een probleem met het nieuwere Android-toestemmingssysteem. Hoogstwaarschijnlijk werkt automatisch exporteren nu niet.
+ AutoExport is niet ingeschakeld.
+ AutoExport is ingeschakeld.
+ AutoExport is (oorspronkelijk) gepland voor %1$s
+ Laatste AutoExport: %1$s
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index d051119c2..bed0b2fe6 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -1241,7 +1241,7 @@
Odrzucaj połączenia na zegarku z wiadomością SMS
Przesyłanie i pobieranie plików
Konfiguracja funkcji przycisków fizycznych zegarka
- AGPS Bundle
+ Pakiet AGPS
100 kroków
10 kroków
1 krok
@@ -1268,7 +1268,7 @@
Umożliwia innym aplikacjom Androida na przesyłanie/nadpisywanie plików
Ustawienia i funkcje wykorzystywane przez programistów
Opcje programistyczne
- Barometer
+ Barometr
Używaj czarnego tła w ciemnym motywie
Edycja połączonej ścieżki GPX
Ustaw
@@ -1453,12 +1453,12 @@
Szczegóły przypomnienia
Opcje wykrywania i parowania
Długie naciśnięcie przycisku powoduje zapisanie ustawień
- Ustawienia
+ Ustawienia wstępne
Minimalne tętno
Wyłączony
Przód
System
- Pozostałe
+ Inne
Bose QC35
VESC
Maksymalne tętno
@@ -1467,7 +1467,7 @@
Dodaj urządzenie testowe
Dystans pod górę
Dystans w dół
- Maksymalny Krok
+ Maksymalny krok
Wspinaczka
Sony SWR12
Sony WF-SP800N
@@ -1486,7 +1486,7 @@
Usuń przypomnienie
Urządzenie nie ma wolnych slotów na przypomnienia (całkowita liczba slotów: %1$s)
Sony WH-1000XM4
- Zatwierdź %s
+ Zatwierdzenie %s
NIEOBSŁUGIWANE
Odkryj nieobsługiwane urządzenia
Średnia
@@ -1518,11 +1518,11 @@
Zrelaksowany
Niestandardowe
Niestandardowy 2
- Zespoły
+ Pasma
Niestandardowe ustawienie 1
Niestandardowe ustawienie 2
Czysty bas
- Upsampling dzięku
+ Upsampling dźwięku
Czułość wykrywania głosu
Automatyczny
Wysoki
@@ -1573,4 +1573,35 @@
automatyczny
Menu
Domyos T540
+ Nieznany
+ Amazfit Pop Pro
+ Start
+ Amazfit Pop
+ Kodek audio
+ Informacje o urządzeniu
+ Ciśnienie atmosferyczne
+ Analizowanie…
+ Zoptymalizuj
+ Nic
+ Optymalizator redukcji szumów
+ Nazwa pakietu aplikacji OpenTracks
+ Używane do uruchamiania/zatrzymywania rejestracji trasy GPS w zewnętrznej aplikacji fitness.
+ Kliknij, aby uruchomić optymalizator redukcji szumów.
+ Optymalizator redukcji szumów
+ Uruchamianie…
+ Nieuruchomiony
+ Pomiar ciśnienia atmosferycznego…
+ Kończenie…
+ Mierzenie warunków użytkowania…
+ Rozpoczęcie śledzenia aplikacji fitness
+ Ustawienia powiadomień Androida
+ Ostatni automatyczny eksport: %1$s
+ Automatyczny eksport jest włączony.
+ Automatyczny eksport jest wyłączony.
+ Automatyczny eksport został zaplanowany na %1$s
+ Automatyczny eksport nie został zaplanowany.
+ Używaj słuchawek tak, jak zwykle. Jeśli warunki użytkowania lub ciśnienie powietrza ulegną zmianie, uruchom optymalizator ponownie.
+ Zatrzymanie śledzenia aplikacji fitness
+ Wstępne ustawienie położenia na %s
+ Lokalizacja nie jest dostępna. Najprawdopodobniej jest to spowodowane nowym systemem uprawnień Androida. Prawdopodobnie automatyczny eksport teraz nie działa.
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 7cd1245f9..a411025d3 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -70,7 +70,7 @@
Suporte a notificações genéricas
…quando a tela estiver ligada
Não perturbar
- Notificações indesejadas são bloqueadas neste modo
+ Bloqueia todas as notificações quando o modo Não pertubar está ativo no telefone
Transliteração
Ative isso se seu dispositivo não tiver suporte à fonte do seu idioma
Sempre
@@ -938,7 +938,7 @@
A inicialização do serviço de segundo plano falhou por causa de uma exceção. Erro:
Falha ao iniciar o serviço de segundo plano
Estresse
- Período
+ Acompanhamento de ciclo
Respiração
Mi Band 5
TLW64
@@ -1461,4 +1461,30 @@
Tempo limite de atualização em minutos:
Ocultar texto ao exceder o tempo limite:
Mostrar círculo ao exceder o tempo limite:
+ Tem a certeza que deseja desligar o dispositivo\?
+ %1$s, todas as semanas
+ %1$s, todos os meses
+ Uma vez
+ %1$s, todos os dias
+ %1$s, todos os anos
+ Todas as semanas
+ Todos os dias
+ Todos os meses
+ Todos os anos
+ Excluir lembrete
+ Desconhecido
+ Desligar
+ Desligar
+ Configurar lembretes
+ Configurar lembretes
+ Repetir
+ Data
+ Hora
+ Mensagem
+ Configurações de notificação de Android
+ %1$s, uma vez
+ Tem certeza de que deseja excluir o lembrete\?
+ Não há posição livre
+ Busca por Bluetooth:
+ Busca por Bluetooth LE:
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index b85529b8e..74efd9cd1 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1566,4 +1566,14 @@
Tempo limite
Largura:
Sony SWR12
+ Desconhecido
+ Começar
+ Optimizar
+ Optimizador de cancelamento de ruído
+ Amazfit Pop
+ Amazfit Pop Pro
+ Clique para começar o optimizador de cancelamento de ruído.
+ Optimizador de cancelamento de ruído
+ Pressão atmosférica
+ Coloque os headphones como os utilizaria normalmente. Se as condições de utilização ou pressão atmosférica alterarem, execute o optimizador novamente.
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 30c932fcc..a6c394052 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -523,7 +523,7 @@
Прошивка
Погода (ярлык)
Будильник
- Таймер
+ Обратный отсчёт
Велопрогулка
Тестовое устройство
Mi Band
@@ -1332,7 +1332,7 @@
Выключить
Вы уверены, что хотите выключить браслет/часы\?
Добавить тестовые часы/браслет
-
+
Показать/Отключить (шаги, расстояние, сон)
Сон
Показывать продолжительность сна
@@ -1352,4 +1352,87 @@
3 часа
Отключен
Эквалайзер
+ %1$s, каждый месяц
+ %1$s, каждую неделю
+ Показывать шаги, пройденное расстояние и продолжительность сна на карточке устройства
+ Настроить напоминания
+ Другое
+ Настроить напоминания
+ Настройки напоминания
+ Расстояние вычисляется по количеству шагов и ширине шага (настраивается в разделе Настройки - About you)
+ Используйте список приложения для того чтобы...
+ Повтор
+ Дата
+ Время
+ Сообщение
+ %1$s, ежедневно
+ %1$s, один раз
+ %1$s, каждый год
+ Один раз
+ Каждый день
+ Каждую неделю
+ Каждый месяц
+ Каждый год
+ Удалить напоминание
+ Вы уверены что хотите удалить это напоминание\?
+ Нет свободных слотов
+ Нет свободных слотов для напоминаний (всего слотов: %1$s)
+ Система
+ Концертный зал
+ Аудиорежим
+ 30 минут
+ Galaxy Buds Live
+ Стандартный (30с)
+ Короткий (15с)
+ Выкл
+ ###.#км
+ ###фт
+ ###м
+ Выкл
+ Средний каденс
+ Долгий (1м)
+ Режим
+ 6,3к
+ Яркий
+ Выкл
+ Анализ…
+ Клуб
+ Арена
+ Шумоподавление
+ Galaxy Buds
+ Частота измерений
+ Включить уведомления
+ Выкл
+ Включить вибрацию
+ Информация об устройстве
+ Аудиокодек
+ Энергосбережение
+ Калории
+ Батарея
+ Минимум
+ Неизвестно
+ Выкл
+ Amazfit Pop
+ Sony WH-1000XM4
+ Amazfit Pop Pro
+ Sony WF-SP800N
+ Domyos T540
+ шагов/мин
+ 1к
+ Sony WH-1000XM3
+ Режим Dolby
+ Время сна
+ Выкл
+ 5 минут
+ Bose QC35
+ VESC
+ Коммит %s
+ Чистый бас
+ 16к
+ 2,5к
+ 400
+ Меню
+ Ширина:
+ Игровой режим
+ Эквалайзер
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 5f103f0dd..3a2ab4b7b 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1596,4 +1596,17 @@
Bitiriliyor…
Amazfit Pop Pro
Amazfit Pop
+ Hiçbir Şey
+ OpenTracks paket adı
+ Harici fitness uygulamasında GPS izleme kaydını başlatmak/durdurmak için kullanılır.
+ Fitness Uygulaması İzlemesini Durdur
+ Fitness Uygulaması İzlemesini Başlat
+ Android bildirim ayarları
+ Son Otomatik Dışa Aktarma: %1$s
+ Otomatik dışa aktarma etkin değil.
+ Otomatik dışa aktarma (başlangıçta) %1$s için zamanlandı
+ Otomatik dışa aktarma zamanlanmadı.
+ Otomatik dışa aktarma etkin.
+ Konum anlaşılamadı. Bu, muhtemelen daha yeni bir Android izin sistemi sorunudur. Büyük olasılıkla, otomatik dışa aktarma şu anda çalışmıyor.
+ konumu %s olarak ön ayarlama
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index bee11b316..1b8d399aa 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -1587,4 +1587,17 @@
Завершення…
Amazfit Pop
Amazfit Pop Pro
+ Порожньо
+ Назва пакунка OpenTracks
+ Використовується для запуску/зупинки запису треків GPS у зовнішньому фітнес-застосунку.
+ Початок відстеження застосунку для фітнесу
+ Зупинка відстеження застосунку для фітнесу
+ Налаштування сповіщень Android
+ Автоекспорт (початково) було заплановано на %1$s
+ Автоекспорт увімкнено.
+ Автоекспорт не ввімкнено.
+ Автоекспорт не заплановано.
+ Останній автоекспорт: %1$s
+ Не вдалося встановити місцеперебування. Ймовірно, проблема з новішою системою дозволів Android. Швидше за все, автоекспорт зараз не працює.
+ попереднє встановлення розташування на %s
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 9a41cdf7a..ea6d5dcdf 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -1585,4 +1585,17 @@
正在开始…
华米 Pop Pro
华米 Pop
+ 无
+ OpenTracks 包名
+ 用于启动/停止外部健身应用的 GPS 追踪记录。
+ Fitness 应用追踪开始
+ Fitness 应用追踪结束
+ Android 通知设置
+ 为 %1$s 自动导出(原始的)已经启用计划
+ 尚未计划自动导出。
+ 位置无法理解。 可能是较新的 Android 权限系统的问题。 最有可能的是,自动导出现在不起作用。
+ 最后一次自动导出:%1$s
+ 自动导出未开启。
+ 自动导出已开启。
+ 预设位置为 %s
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 53de08037..1c761d590 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -142,7 +142,7 @@
啟用 VoIP 應用程式電話通知
最低通知間隔時間
在螢幕開啟時仍要通知
- 在此模式下將停止不必要的通知
+ 當手機啟用請勿打擾時阻止所有通知
應用程式黑名單
開發人員選項
第三方即時心率資料存取
@@ -296,8 +296,8 @@
小米手環 4
狀態
通知
- 活動
- 計時器
+ 鍛煉歷史
+ 倒數
更多
NFC
步數:%1$02d
@@ -334,9 +334,14 @@
手環將在您一段時間未活動時震動
未活動閾值(分鐘)
在時間內停用久坐提醒
- 資料庫的存取將使用下列在裝置上的路徑。
-\n此路徑可供其他 Android 應用程式及您的電腦存取。
-\n您可以在那裏找到已匯出的資料庫(或放入要匯入的資料庫):
+ 匯出/匯入動作將使用這些(見下方)在裝置上的路徑。此資料夾可供其他 Android 應用程式及您的電腦存取。請注意,如果您移除 Gadgetbridge,此目錄和所有內含檔案都會被刪除。這些資料包含:
+\n Export_preference - 全域設定
+\n Export_preference_MAC - 裝置額外設定
+\n Gadgetbridge - 裝置及活動的資料庫
+\n Gadgetbridge_date - 在某個日期匯出的資料庫
+\n *.gpx - GPS 紀錄
+\n *.log - 記錄檔
+\n您可以在那裏找到已匯出的檔案(或放入要匯入的檔案):
匯出資料
匯入資料
匯出和匯入
@@ -485,7 +490,7 @@
小時:
分鐘:
羅盤
- Gadgetbridge 通知
+ 一般
睿米3
睿米
Watch 9
@@ -578,7 +583,7 @@
額外裝置支援
貢獻者
核心團隊(按第一次程式碼貢獻順序排列)
- 一款免費且離線的替代品,用來取代您的裝置廠商提供的閉源 Android 應用程式。
+ 用來取代廠商提供的閉源 Android 小裝置應用程式的離線且版權自由的替代品。
關於 Gadgetbridge
關於
運動活動
@@ -648,4 +653,39 @@
電池資訊
設定別名
尋找 %1$s\?
+ 關機
+ 您確定要關閉裝置電源嗎?
+ 勾選所有應用程式
+ 取消勾選所有應用程式
+ 拒絕來自所選應用程式的通知
+ 其他
+ 系統
+ 訊息隱私模式
+ 顯示內容
+ 隱藏內容
+ 關機
+ 在請勿打擾期間啟用抬腕顯示
+ 在裝置卡上顯示目前步數、距離或睡眠
+ 通知設定
+ 使用應用程式清單來...
+ 應用程式清單
+ 允許來自所選應用程式的通知
+ 個別應用程式設定
+ 正在嘗試連線到: %1$s
+ 探索和配對選項
+ 嘗試在執行時獲取目前位置,使用儲存的位置作為後備
+ 在深色主題中使用黑色背景
+ 自動提取
+ 選擇在裝置卡上要顯示哪些活動詳細資訊
+ 裝置卡上的活動狀態
+ 在裝置卡上顯示活動資訊
+ 睡眠
+ 顯示睡眠時間
+ 距離是根據步數和步長計算出的(可以在設定 - 關於你中調整)
+ 顯示總步數
+ 啟用此選項將在掃描時顯示所有發現的藍芽裝置。短按會將設備名稱和 mac 位址複製到剪貼簿。長按將開啟「新增測試裝置」對話框。
+ 探索不支援的裝置
+ 根據系統
+ 藍芽掃描:
+ 低功耗藍芽掃描:
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 3438d69ad..36387bf1f 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -1654,6 +1654,12 @@
- @string/pref_media_forward
- @string/pref_media_rewind
- @string/pref_device_action_broadcast
+ - @string/pref_device_action_fitness_app_control_start
+
@@ -1668,6 +1674,12 @@
- @string/pref_media_forward_value
- @string/pref_media_rewind_value
- @string/pref_device_action_broadcast_value
+ - @string/pref_device_action_fitness_app_control_start_value
+
@@ -1676,6 +1688,12 @@
- @string/pref_media_pause
- @string/pref_media_playpause
- @string/pref_device_action_broadcast
+ - @string/pref_device_action_fitness_app_control_start
+
@@ -1684,6 +1702,12 @@
- @string/pref_media_pause_value
- @string/pref_media_playpause_value
- @string/pref_device_action_broadcast_value
+ - @string/pref_device_action_fitness_app_control_start_value
+
@@ -2085,5 +2109,13 @@
- mode_ask
- mode_auto
+
+
+ - de.dennisguse.opentracks
+ - de.dennisguse.opentracks.playStore
+ - de.dennisguse.opentracks.debug
+ - de.dennisguse.opentracks.nightly
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4d68dc91c..c6d82b9f3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -142,6 +142,7 @@
Pebble Messages
Support for apps that send notifications to the Pebble via PebbleKit.
Generic notification support
+ Android notification settings
…also when screen is on
Do Not Disturb
Block all notifications when Do Not Disturb is enabled on the phone
@@ -759,7 +760,13 @@
Old Activity database deletion failed.
Overwrite
Database autoexport location has been set to:
+ Last AutoExport: %1$s
+ AutoExport is enabled.
+ AutoExport is not enabled.
+ AutoExport has (originally) been scheduled for %1$s
+ AutoExport has not been not scheduled.
AutoExport
+ Location could not be understood. Likely an issue of newer Android permission system. Most likely, autoexport is not working now.
Export Data
Import Data
Run AutoExport Now
@@ -1495,6 +1502,8 @@
Skip forward
Skip back
Send Broadcast
+ Fitness App Tracking Start
+ Fitness App Tracking Stop
###m
@@ -1516,4 +1525,8 @@
Menu
Some buttons cannot be configured because their functions are hard-coded in the watch firmware.\n\nWarning: long-pressing the upper button when a watchface from the official Fossil app is installed will also toggle between showing/hiding widgets.
Width:
+ OpenTracks package name
+ Used for starting/stopping GPS track recording in external fitness app.
+ pre-setting position to %s
+ Light up on new notification
diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml
index d606a27a0..d8343afb7 100644
--- a/app/src/main/res/values/values.xml
+++ b/app/src/main/res/values/values.xml
@@ -106,6 +106,8 @@
- FORWARD
- REWIND
- BROADCAST
+ - FITNESS_CONTROL_START
+ - FITNESS_CONTROL_STOP
- Voice Assistant
- Active Noise Cancelling
- Quick Ambient Sound
diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml
index 71aecff13..6d845686b 100644
--- a/app/src/main/res/xml/changelog_master.xml
+++ b/app/src/main/res/xml/changelog_master.xml
@@ -1,5 +1,26 @@
+
+ Add basic support for Casio GBD-H1000
+ Add support for Hama Fit Track 1900 - via FitPro device support
+ Add OpenTracksController for interactions with OpenTracks
+ Fossil Hybrid HR: Start/stop track in OpenTracks from GPS workout on watch
+ Fossil Hybrid HR: Try guessing new widget position
+ Fossil Hybrid HR: Allow assigning no function to a button
+ Add Huami button/device action to control fitness tracking via OpenTracksController
+ Mi Band 6: Sync alarms set on the watch like on Amazfit Bip U and others
+ Bangle.js: Handle battery charging status and fix battery chart.
+ Bangle.js: Prevent exception in case UART RX line is empty
+ Bangle.js: Add repetitions in alarm JSON
+ WaspOS: Fix battery chart.
+ WaspOS: Add condition code to weather JSON
+ XWatch: Add notifications and calls support
+ UM-25: Make cumulative values resettable
+ VESC: Fixed crash when loading a saved value
+ Allow to open Android notification settings from Notification settings
+ AutoExporter changes for better operation and troubleshooting
+ Change Nightly icons background color
+
Amazfit Pop/Pro: Initial Support (probably the same as Bip U but has a different firmware)
Sony WH-1000XM4: Initial Support
diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr.xml
index f0c9c022b..cc36f29d4 100644
--- a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml
+++ b/app/src/main/res/xml/devicesettings_fossilhybridhr.xml
@@ -41,11 +41,12 @@
android:key="button_3_function_short"
android:summary="%s"
android:title="@string/pref_title_lower_button_function_short" />
-
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/notifications_preferences.xml b/app/src/main/res/xml/notifications_preferences.xml
index fe283c7fd..33f4ac10f 100644
--- a/app/src/main/res/xml/notifications_preferences.xml
+++ b/app/src/main/res/xml/notifications_preferences.xml
@@ -3,6 +3,10 @@
android:key="pref_key_notifications"
android:title="@string/pref_header_notifications">
+
+
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 6b930337c..c0b21f207 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -141,6 +141,10 @@
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_location_keep_uptodate"
android:title="@string/pref_title_location_keep_uptodate" />
+
diff --git a/app/src/nightly/res/values/ic_launcher_background.xml b/app/src/nightly/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..cdde88fb8
--- /dev/null
+++ b/app/src/nightly/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #000000
+
diff --git a/app/src/nopebble/res/values/ic_launcher_background.xml b/app/src/nopebble/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..ed621a247
--- /dev/null
+++ b/app/src/nopebble/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #ff3d00
+
diff --git a/external/fossil-hr-watchface b/external/fossil-hr-watchface
index 44a70cf7c..aad2a141c 160000
--- a/external/fossil-hr-watchface
+++ b/external/fossil-hr-watchface
@@ -1 +1 @@
-Subproject commit 44a70cf7c3a783d07d0fdab8b4b15e677da63af2
+Subproject commit aad2a141cb2e151431f8354e52d9b74f6829855a
diff --git a/fastlane/metadata/android/en-US/changelogs/210.txt b/fastlane/metadata/android/en-US/changelogs/210.txt
new file mode 100644
index 000000000..01d574c72
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/210.txt
@@ -0,0 +1,19 @@
+* Add basic support for Casio GBD-H1000
+* Add support for Hama Fit Track 1900 - via FitPro device support
+* Add OpenTracksController for interactions with OpenTracks
+* Fossil Hybrid HR: Start/stop track in OpenTracks from GPS workout on watch
+* Fossil Hybrid HR: Try guessing new widget position
+* Fossil Hybrid HR: Allow assigning no function to a button
+* Add Huami button/device action to control fitness tracking via OpenTracksController
+* Mi Band 6: Sync alarms set on the watch like on Amazfit Bip U and others
+* Bangle.js: Handle battery charging status and fix battery chart.
+* Bangle.js: Prevent exception in case UART RX line is empty
+* Bangle.js: Add repetitions in alarm JSON
+* WaspOS: Fix battery chart.
+* WaspOS: Add condition code to weather JSON
+* XWatch: Add notifications and calls support
+* UM-25: Make cumulative values resettable
+* VESC: Fixed crash when loading a saved value
+* Allow to open Android notification settings from Notification settings
+* AutoExporter changes for better operation and troubleshooting
+* Change Nightly icons background color