diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2e9976c1..9d29d9d4b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,10 @@
### Changelog
-### NEXT
-* Amazfit Bip U/Pro/Band 5: enable extended HR/stress monitoring setting
+### 0.67.1
+* Huami: Fix long music track names not displaying
+* Amazfit Bip U/Pro/Band 5: Enable extended HR/stress monitoring setting
+* Pebble: Fix calendar blacklist, view and storage
+* FitPro: Fix crash, inactivity warning preference to string
### 0.67.0
* Initial Support for Sony WF-1000XM3
diff --git a/app/build.gradle b/app/build.gradle
index 76d42bd87..15a94424f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -55,8 +55,8 @@ android {
multiDexEnabled true
// Note: always bump BOTH versionCode and versionName!
- versionName "0.67.0"
- versionCode 211
+ versionName "0.67.1"
+ versionCode 212
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
buildConfigField "String", "GIT_HASH_SHORT", "\"${getGitHashShort()}\""
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index dd1335963..0e0ab834b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -595,6 +595,12 @@
+
+
+
+
+
+
diff --git a/app/src/main/assets/fossil_hr/openSourceWatchface.bin b/app/src/main/assets/fossil_hr/openSourceWatchface.bin
index 41e239c15..6358d3cdf 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 085370cb4..82a6e166a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java
@@ -68,7 +68,7 @@ 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.externalevents.opentracks.OpenTracksContentObserver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
@@ -117,7 +117,7 @@ public class GBApplication extends Application {
private static SharedPreferences sharedPrefs;
private static final String PREFS_VERSION = "shared_preferences_version";
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
- private static final int CURRENT_PREFS_VERSION = 14;
+ private static final int CURRENT_PREFS_VERSION = 15;
private static LimitedQueue mIDSenderLookup = new LimitedQueue(16);
private static Prefs prefs;
@@ -559,11 +559,11 @@ public class GBApplication extends Application {
private static HashSet calendars_blacklist = null;
- public static boolean calendarIsBlacklisted(String calendarDisplayName) {
+ public static boolean calendarIsBlacklisted(String calendarUniqueName) {
if (calendars_blacklist == null) {
GB.log("calendarIsBlacklisted: calendars_blacklist is null!", GB.INFO, null);
}
- return calendars_blacklist != null && calendars_blacklist.contains(calendarDisplayName);
+ return calendars_blacklist != null && calendars_blacklist.contains(calendarUniqueName);
}
public static void setCalendarsBlackList(Set calendarNames) {
@@ -577,14 +577,18 @@ public class GBApplication extends Application {
saveCalendarsBlackList();
}
- public static void addCalendarToBlacklist(String calendarDisplayName) {
- if (calendars_blacklist.add(calendarDisplayName)) {
+ public static void addCalendarToBlacklist(String calendarUniqueName) {
+ if (calendars_blacklist.add(calendarUniqueName)) {
+ GB.log("Blacklisted calendar " + calendarUniqueName, GB.INFO, null);
saveCalendarsBlackList();
+ } else {
+ GB.log("Calendar " + calendarUniqueName + " already blacklisted!", GB.WARN, null);
}
}
- public static void removeFromCalendarBlacklist(String calendarDisplayName) {
- calendars_blacklist.remove(calendarDisplayName);
+ public static void removeFromCalendarBlacklist(String calendarUniqueName) {
+ calendars_blacklist.remove(calendarUniqueName);
+ GB.log("Unblacklisted calendar " + calendarUniqueName, GB.INFO, null);
saveCalendarsBlackList();
}
@@ -1143,6 +1147,25 @@ public class GBApplication extends Application {
}
}
+ if (oldVersion < 15) {
+ try (DBHandler db = acquireDB()) {
+ final DaoSession daoSession = db.getDaoSession();
+ final List activeDevices = DBHelper.getActiveDevices(daoSession);
+
+ for (Device dbDevice : activeDevices) {
+ final SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier());
+ final SharedPreferences.Editor deviceSharedPrefsEdit = deviceSharedPrefs.edit();
+
+ if (DeviceType.FITPRO.equals(dbDevice.getType())) {
+ editor.remove("inactivity_warnings_threshold");
+ deviceSharedPrefsEdit.apply();
+ }
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "error acquiring DB lock");
+ }
+ }
+
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
editor.apply();
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java
index 11b054843..c74d3adc7 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java
@@ -24,6 +24,7 @@ import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
+import android.os.Bundle;
import android.widget.RemoteViews;
import android.widget.Toast;
@@ -37,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
+import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
/**
* Implementation of SleepAlarmWidget functionality. When pressing the widget, an alarm will be set
@@ -44,11 +46,10 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
* value is retrieved using ActivityUser.().getSleepDuration().
*/
public class SleepAlarmWidget extends AppWidgetProvider {
-
/**
* This is our dedicated action to detect when the widget has been clicked.
*/
- public static final String ACTION =
+ public static final String ACTION_CLICK =
"nodomain.freeyourgadget.gadgetbridge.SLEEP_ALARM_WIDGET_CLICK";
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
@@ -59,9 +60,10 @@ public class SleepAlarmWidget extends AppWidgetProvider {
// Add our own click intent
Intent intent = new Intent(context, SleepAlarmWidget.class);
- intent.setAction(ACTION);
+ intent.setAction(ACTION_CLICK);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent clickPI = PendingIntent.getBroadcast(
- context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.sleepalarmwidget_text, clickPI);
// Instruct the widget manager to update the widget
@@ -89,7 +91,15 @@ public class SleepAlarmWidget extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
- if (ACTION.equals(intent.getAction())) {
+ Bundle extras = intent.getExtras();
+ int appWidgetId = -1;
+ if (extras != null) {
+ appWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ if (ACTION_CLICK.equals(intent.getAction())) {
int userSleepDuration = new ActivityUser().getSleepDurationGoal();
// current timestamp
GregorianCalendar calendar = new GregorianCalendar();
@@ -102,16 +112,11 @@ public class SleepAlarmWidget extends AppWidgetProvider {
// overwrite the first alarm and activate it, without
- Context appContext = context.getApplicationContext();
- if (appContext instanceof GBApplication) {
- GBApplication gbApp = (GBApplication) appContext;
- GBDevice selectedDevice = gbApp.getDeviceManager().getSelectedDevice();
- if (selectedDevice == null || !selectedDevice.isInitialized()) {
- GB.toast(context,
- context.getString(R.string.appwidget_not_connected),
- Toast.LENGTH_LONG, GB.WARN);
- return;
- }
+ GBDevice deviceForWidget = new WidgetPreferenceStorage().getDeviceForWidget(appWidgetId);
+ if (deviceForWidget == null || !deviceForWidget.isInitialized()) {
+ GB.toast(context, context.getString(R.string.appwidget_not_connected),
+ Toast.LENGTH_SHORT, GB.WARN);
+ return;
}
int hours = calendar.get(Calendar.HOUR_OF_DAY);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java
index 0cbfef767..8121d80dd 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java
@@ -54,11 +54,9 @@ import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.GregorianCalendar;
-import java.util.List;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
-import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.WidgetAlarmsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@@ -77,30 +75,9 @@ public class Widget extends AppWidgetProvider {
private static final Logger LOG = LoggerFactory.getLogger(Widget.class);
static BroadcastReceiver broadcastReceiver = null;
- GBDevice selectedDevice;
-
- private GBDevice getSelectedDevice() {
- Context context = GBApplication.getContext();
- if (!(context instanceof GBApplication)) {
- return null;
- }
- GBApplication gbApp = (GBApplication) context;
- return gbApp.getDeviceManager().getSelectedDevice();
- }
-
- private GBDevice getDeviceByMAC(Context appContext, String HwAddress) {
- GBApplication gbApp = (GBApplication) appContext;
- List extends GBDevice> devices = gbApp.getDeviceManager().getDevices();
- for (GBDevice device : devices) {
- if (device.getAddress().equals(HwAddress)) {
- return device;
- }
- }
- return null;
- }
- private long[] getSteps() {
+ private long[] getSteps(GBDevice gbDevice) {
Context context = GBApplication.getContext();
Calendar day = GregorianCalendar.getInstance();
@@ -108,7 +85,7 @@ public class Widget extends AppWidgetProvider {
return new long[]{0, 0, 0};
}
DailyTotals ds = new DailyTotals();
- return ds.getDailyTotalsForDevice(selectedDevice, day);
+ return ds.getDailyTotalsForDevice(gbDevice, day);
//return ds.getDailyTotalsForAllDevices(day);
}
@@ -119,11 +96,10 @@ public class Widget extends AppWidgetProvider {
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
- selectedDevice = getSelectedDevice();
- WidgetPreferenceStorage widgetPreferenceStorage = new WidgetPreferenceStorage();
- String savedDeviceAddress = widgetPreferenceStorage.getSavedDeviceAddress(context, appWidgetId);
- if (savedDeviceAddress != null) {
- selectedDevice = getDeviceByMAC(context.getApplicationContext(), savedDeviceAddress); //this would probably only happen if device no longer exists in GB
+ GBDevice deviceForWidget = new WidgetPreferenceStorage().getDeviceForWidget(appWidgetId);
+ if (deviceForWidget == null) {
+ LOG.debug("Widget: no device, bailing out");
+ return;
}
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
@@ -143,17 +119,17 @@ public class Widget extends AppWidgetProvider {
//alarms popup menu
Intent startAlarmListIntent = new Intent(context, WidgetAlarmsActivity.class);
- startAlarmListIntent.putExtra(GBDevice.EXTRA_DEVICE, selectedDevice);
+ startAlarmListIntent.putExtra(GBDevice.EXTRA_DEVICE, deviceForWidget);
PendingIntent startAlarmListPIntent = PendingIntent.getActivity(context, appWidgetId, startAlarmListIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.todaywidget_header_alarm_icon, startAlarmListPIntent);
//charts
Intent startChartsIntent = new Intent(context, ChartsActivity.class);
- startChartsIntent.putExtra(GBDevice.EXTRA_DEVICE, selectedDevice);
+ startChartsIntent.putExtra(GBDevice.EXTRA_DEVICE, deviceForWidget);
PendingIntent startChartsPIntent = PendingIntent.getActivity(context, appWidgetId, startChartsIntent, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.todaywidget_bottom_layout, startChartsPIntent);
- long[] dailyTotals = getSteps();
+ long[] dailyTotals = getSteps(deviceForWidget);
int steps = (int) dailyTotals[0];
int sleep = (int) dailyTotals[1];
ActivityUser activityUser = new ActivityUser();
@@ -165,6 +141,10 @@ public class Widget extends AppWidgetProvider {
double distanceMeters = dailyTotals[0] * stepLength * 0.01;
String distanceFormatted = FormatUtils.getFormattedDistanceLabel(distanceMeters);
+ if (sleep < 1) {
+ views.setViewVisibility(R.id.todaywidget_sleep_layout, View.GONE);
+ }
+
views.setTextViewText(R.id.todaywidget_steps, String.format("%1s", steps));
views.setTextViewText(R.id.todaywidget_sleep, String.format("%1s", getHM(sleep)));
views.setTextViewText(R.id.todaywidget_distance, distanceFormatted);
@@ -172,17 +152,17 @@ public class Widget extends AppWidgetProvider {
views.setProgressBar(R.id.todaywidget_sleep_progress, sleepGoalMinutes, sleep, false);
views.setProgressBar(R.id.todaywidget_distance_progress, distanceGoal, steps * stepLength, false);
views.setViewVisibility(R.id.todaywidget_battery_icon, View.GONE);
- if (selectedDevice != null) {
- String status = String.format("%1s", selectedDevice.getStateString());
- if (selectedDevice.isConnected()) {
- if (selectedDevice.getBatteryLevel() > 1) {
+ if (deviceForWidget != null) {
+ String status = String.format("%1s", deviceForWidget.getStateString());
+ if (deviceForWidget.isConnected()) {
+ if (deviceForWidget.getBatteryLevel() > 1) {
views.setViewVisibility(R.id.todaywidget_battery_icon, View.VISIBLE);
- status = String.format("%1s%%", selectedDevice.getBatteryLevel());
+ status = String.format("%1s%%", deviceForWidget.getBatteryLevel());
}
}
- String deviceName = selectedDevice.getAlias() != null ? selectedDevice.getAlias() : selectedDevice.getName();
+ String deviceName = deviceForWidget.getAlias() != null ? deviceForWidget.getAlias() : deviceForWidget.getName();
views.setTextViewText(R.id.todaywidget_device_status, status);
views.setTextViewText(R.id.todaywidget_device_name, deviceName);
}
@@ -191,11 +171,11 @@ public class Widget extends AppWidgetProvider {
appWidgetManager.updateAppWidget(appWidgetId, views);
}
- public void refreshData() {
+ public void refreshData(int appWidgetId) {
Context context = GBApplication.getContext();
- GBDevice device = getSelectedDevice();
+ GBDevice deviceForWidget = new WidgetPreferenceStorage().getDeviceForWidget(appWidgetId);
- if (device == null || !device.isInitialized()) {
+ if (deviceForWidget == null || !deviceForWidget.isInitialized()) {
GB.toast(context,
context.getString(R.string.device_not_connected),
Toast.LENGTH_SHORT, GB.ERROR);
@@ -265,7 +245,7 @@ public class Widget extends AppWidgetProvider {
super.onReceive(context, intent);
LOG.debug("gbwidget LOCAL onReceive, action: " + intent.getAction() + intent);
Bundle extras = intent.getExtras();
- int appWidgetId = -1;
+ int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
if (extras != null) {
appWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
@@ -277,7 +257,7 @@ public class Widget extends AppWidgetProvider {
if (broadcastReceiver == null) {
onEnabled(context);
}
- refreshData();
+ refreshData(appWidgetId);
//updateWidget();
} else if (APPWIDGET_DELETED.equals(intent.getAction())) {
onDisabled(context);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java
index cb2744ab8..cfdf1afbf 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java
@@ -51,6 +51,7 @@ public class CalBlacklistActivity extends AbstractGBActivity {
private final String[] EVENT_PROJECTION = new String[]{
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
+ CalendarContract.Calendars.ACCOUNT_NAME,
CalendarContract.Calendars.CALENDAR_COLOR
};
private ArrayList calendarsArrayList;
@@ -69,7 +70,7 @@ public class CalBlacklistActivity extends AbstractGBActivity {
try (Cursor cur = getContentResolver().query(uri, EVENT_PROJECTION, null, null, null)) {
calendarsArrayList = new ArrayList<>();
while (cur != null && cur.moveToNext()) {
- calendarsArrayList.add(new Calendar(cur.getString(0), cur.getInt(1)));
+ calendarsArrayList.add(new Calendar(cur.getString(0), cur.getString(1), cur.getInt(2)));
}
}
@@ -82,9 +83,9 @@ public class CalBlacklistActivity extends AbstractGBActivity {
CheckBox selected = (CheckBox) view.findViewById(R.id.item_checkbox);
toggleEntry(view);
if (selected.isChecked()) {
- GBApplication.addCalendarToBlacklist(item.displayName);
+ GBApplication.addCalendarToBlacklist(item.getUniqueString());
} else {
- GBApplication.removeFromCalendarBlacklist(item.displayName);
+ GBApplication.removeFromCalendarBlacklist(item.getUniqueString());
}
}
});
@@ -112,12 +113,18 @@ public class CalBlacklistActivity extends AbstractGBActivity {
class Calendar {
private final String displayName;
+ private final String accountName;
private final int color;
- public Calendar(String displayName, int color) {
+ public Calendar(String displayName, String accountName, int color) {
this.displayName = displayName;
+ this.accountName = accountName;
this.color = color;
}
+
+ public String getUniqueString() {
+ return accountName + '/' + displayName;
+ }
}
private class CalendarListAdapter extends ArrayAdapter {
@@ -138,13 +145,16 @@ public class CalBlacklistActivity extends AbstractGBActivity {
View color = view.findViewById(R.id.calendar_color);
TextView name = (TextView) view.findViewById(R.id.calendar_name);
+ TextView ownerAccount = (TextView) view.findViewById(R.id.calendar_owner_account);
CheckBox checked = (CheckBox) view.findViewById(R.id.item_checkbox);
- if (GBApplication.calendarIsBlacklisted(item.displayName) && !checked.isChecked()) {
+ if (GBApplication.calendarIsBlacklisted(item.getUniqueString()) && !checked.isChecked() ||
+ !GBApplication.calendarIsBlacklisted(item.getUniqueString()) && checked.isChecked()) {
toggleEntry(view);
}
color.setBackgroundColor(item.color);
name.setText(item.displayName);
+ ownerAccount.setText(item.accountName);
return view;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java
index 640d12359..0e9a24e0e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java
@@ -348,30 +348,30 @@ public class ControlCenterv2 extends AppCompatActivity
case R.id.action_settings:
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivityForResult(settingsIntent, MENU_REFRESH_CODE);
- return true;
+ return false; //we do not want the drawer menu item to get selected
case R.id.action_debug:
Intent debugIntent = new Intent(this, DebugActivity.class);
startActivity(debugIntent);
- return true;
+ return false;
case R.id.action_data_management:
Intent dbIntent = new Intent(this, DataManagementActivity.class);
startActivity(dbIntent);
- return true;
+ return false;
case R.id.action_notification_management:
Intent blIntent = new Intent(this, NotificationManagementActivity.class);
startActivity(blIntent);
- return true;
+ return false;
case R.id.device_action_discover:
launchDiscoveryActivity();
- return true;
+ return false;
case R.id.action_quit:
GBApplication.quit();
- return true;
+ return false;
case R.id.donation_link:
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://liberapay.com/Gadgetbridge")); //TODO: centralize if ever used somewhere else
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
- return true;
+ return false;
case R.id.external_changelog:
ChangeLog cl = createChangeLog();
try {
@@ -379,14 +379,14 @@ public class ControlCenterv2 extends AppCompatActivity
} catch (Exception ignored) {
GB.toast(getBaseContext(), "Error showing Changelog", Toast.LENGTH_LONG, GB.ERROR);
}
- return true;
+ return false;
case R.id.about:
Intent aboutIntent = new Intent(this, AboutActivity.class);
startActivity(aboutIntent);
- return true;
+ return false;
}
- return true;
+ return false;
}
private ChangeLog createChangeLog() {
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 29957ada4..d34ec7760 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
@@ -71,8 +71,6 @@ 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;
@@ -86,8 +84,9 @@ 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.externalevents.gps.GBLocationManager;
+import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksContentObserver;
+import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksController;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
@@ -529,6 +528,14 @@ public class DebugActivity extends AbstractGBActivity {
}
});
+ Button stopPhoneGpsLocationListener = findViewById(R.id.stopPhoneGpsLocationListener);
+ stopPhoneGpsLocationListener.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ GBLocationManager.stopAll(getBaseContext());
+ }
+ });
+
Button showStatusFitnessAppTracking = findViewById(R.id.showStatusFitnessAppTracking);
final int delay = 2 * 1000;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java
new file mode 100644
index 000000000..a2e623549
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java
@@ -0,0 +1,139 @@
+package nodomain.freeyourgadget.gadgetbridge.activities;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Pair;
+import android.widget.ListView;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
+import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.Device;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
+import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
+
+public class SleepAlarmWidgetConfigurationActivity extends Activity {
+
+ // modified copy of WidgetConfigurationActivity
+ // if we knew which widget is calling this config activity, we could only use a single configuration
+ // activity and customize the filter in getAllDevices based on the caller.
+
+ private static final Logger LOG = LoggerFactory.getLogger(SleepAlarmWidgetConfigurationActivity.class);
+ int mAppWidgetId;
+
+ LinkedHashMap> allDevices;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setResult(RESULT_CANCELED);
+
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+
+ if (extras != null) {
+ mAppWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+ // make the result intent and set the result to canceled
+ Intent resultValue;
+ resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_CANCELED, resultValue);
+
+ if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(SleepAlarmWidgetConfigurationActivity.this);
+ builder.setTitle(R.string.widget_settings_select_device_title);
+
+ allDevices = getAllDevices(getApplicationContext());
+
+ List list = new ArrayList<>();
+ for (Map.Entry> item : allDevices.entrySet()) {
+ list.add(item.getKey());
+ }
+ String[] allDevicesString = list.toArray(new String[0]);
+
+ builder.setSingleChoiceItems(allDevicesString, 0, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ }
+ });
+
+ builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ListView lw = ((AlertDialog) dialog).getListView();
+ int selectedItemPosition = lw.getCheckedItemPosition();
+
+ if (selectedItemPosition > -1) {
+ Map.Entry> selectedItem =
+ (Map.Entry>) allDevices.entrySet().toArray()[selectedItemPosition];
+ WidgetPreferenceStorage widgetPreferenceStorage = new WidgetPreferenceStorage();
+ widgetPreferenceStorage.saveWidgetPrefs(getApplicationContext(), String.valueOf(mAppWidgetId), selectedItem.getValue().first);
+ }
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
+ finish();
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent resultValue;
+ resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_CANCELED, resultValue);
+ finish();
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ public LinkedHashMap getAllDevices(Context appContext) {
+ DaoSession daoSession;
+ GBApplication gbApp = (GBApplication) appContext;
+ LinkedHashMap> newMap = new LinkedHashMap<>(1);
+ List extends GBDevice> devices = gbApp.getDeviceManager().getDevices();
+
+ try (DBHandler handler = GBApplication.acquireDB()) {
+ daoSession = handler.getDaoSession();
+ for (GBDevice device : devices) {
+ DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
+ Device dbDevice = DBHelper.findDevice(device, daoSession);
+ int icon = device.isInitialized() ? device.getType().getIcon() : device.getType().getDisabledIcon();
+ if (dbDevice != null && coordinator != null
+ && (coordinator.getAlarmSlotCount() > 0)
+ && !newMap.containsKey(device.getAliasOrName())) {
+ newMap.put(device.getAliasOrName(), new Pair(device.getAddress(), icon));
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Error getting list of all devices: " + e);
+ }
+ return newMap;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetAlarmsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetAlarmsActivity.java
index 59ee7ff21..f078a6f21 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetAlarmsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetAlarmsActivity.java
@@ -40,17 +40,17 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class WidgetAlarmsActivity extends Activity implements View.OnClickListener {
TextView textView;
+ GBDevice deviceForWidget;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context appContext = this.getApplicationContext();
- GBDevice selectedDevice;
Bundle extras = getIntent().getExtras();
if (extras != null) {
- selectedDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
+ deviceForWidget = extras.getParcelable(GBDevice.EXTRA_DEVICE);
} else {
GB.toast(this,
"Error no device",
@@ -59,9 +59,8 @@ public class WidgetAlarmsActivity extends Activity implements View.OnClickListen
}
if (appContext instanceof GBApplication) {
- GBApplication gbApp = (GBApplication) appContext;
- if (selectedDevice == null || !selectedDevice.isInitialized()) {
+ if (deviceForWidget == null || !deviceForWidget.isInitialized()) {
GB.toast(this,
this.getString(R.string.not_connected),
Toast.LENGTH_LONG, GB.INFO);
@@ -128,16 +127,11 @@ public class WidgetAlarmsActivity extends Activity implements View.OnClickListen
// overwrite the first alarm and activate it, without
- Context appContext = this.getApplicationContext();
- if (appContext instanceof GBApplication) {
- GBApplication gbApp = (GBApplication) appContext;
- GBDevice selectedDevice = gbApp.getDeviceManager().getSelectedDevice();
- if (selectedDevice == null || !selectedDevice.isInitialized()) {
- GB.toast(this,
- this.getString(R.string.appwidget_not_connected),
- Toast.LENGTH_LONG, GB.WARN);
- return;
- }
+ if (deviceForWidget == null || !deviceForWidget.isInitialized()) {
+ GB.toast(this,
+ this.getString(R.string.appwidget_not_connected),
+ Toast.LENGTH_LONG, GB.WARN);
+ return;
}
int hours = calendar.get(Calendar.HOUR_OF_DAY);
@@ -152,6 +146,5 @@ public class WidgetAlarmsActivity extends Activity implements View.OnClickListen
alarms.add(alarm);
GBApplication.deviceService().onSetAlarms(alarms);
-
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java
index 8ffea8c00..b89d83ce9 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java
@@ -31,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
public class WidgetConfigurationActivity extends Activity {
- private static final Logger LOG = LoggerFactory.getLogger(Widget.class);
+ private static final Logger LOG = LoggerFactory.getLogger(WidgetConfigurationActivity.class);
int mAppWidgetId;
LinkedHashMap> allDevices;
@@ -44,6 +44,7 @@ public class WidgetConfigurationActivity extends Activity {
Intent intent = getIntent();
Bundle extras = intent.getExtras();
+
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsActivity.java
index 9ed08de5d..0a74095c3 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsActivity.java
@@ -17,6 +17,7 @@
package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings;
import android.os.Bundle;
+import android.view.MenuItem;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceFragmentCompat;
@@ -72,4 +73,17 @@ public class DeviceSettingsActivity extends AbstractGBActivity implements
.commit();
return true;
}
-}
\ No newline at end of file
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // Simulate a back press, so that we don't actually exit the activity when
+ // in a nested PreferenceScreen
+ this.onBackPressed();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
index d2ff3f607..a07af38d8 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
@@ -78,6 +78,7 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_INACTIVITY_START = "inactivity_warnings_start";
public static final String PREF_INACTIVITY_END = "inactivity_warnings_end";
public static final String PREF_INACTIVITY_THRESHOLD = "inactivity_warnings_threshold";
+ public static final String PREF_INACTIVITY_THRESHOLD_EXTENDED = "inactivity_warnings_threshold_extended";
public static final String PREF_INACTIVITY_MO = "inactivity_warnings_mo";
public static final String PREF_INACTIVITY_TU = "inactivity_warnings_tu";
public static final String PREF_INACTIVITY_WE = "inactivity_warnings_we";
@@ -116,6 +117,9 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_DO_NOT_DISTURB_AUTOMATIC = "automatic";
public static final String PREF_DO_NOT_DISTURB_SCHEDULED = "scheduled";
+ public static final String PREF_WORKOUT_START_ON_PHONE = "workout_start_on_phone";
+ public static final String PREF_WORKOUT_SEND_GPS_TO_BAND = "workout_send_gps_to_band";
+
public static final String PREF_FIND_PHONE = "prefs_find_phone";
public static final String PREF_FIND_PHONE_DURATION = "prefs_find_phone_duration";
public static final String PREF_AUTOLIGHT = "autolight";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
index efe23cbca..53808daff 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
@@ -429,6 +429,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
addPreferenceHandlerFor(PREF_INACTIVITY_START);
addPreferenceHandlerFor(PREF_INACTIVITY_END);
addPreferenceHandlerFor(PREF_INACTIVITY_THRESHOLD);
+ addPreferenceHandlerFor(PREF_INACTIVITY_THRESHOLD_EXTENDED);
addPreferenceHandlerFor(PREF_INACTIVITY_MO);
addPreferenceHandlerFor(PREF_INACTIVITY_TU);
addPreferenceHandlerFor(PREF_INACTIVITY_WE);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java
index a0ec99c1c..f8674afd8 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java
@@ -18,6 +18,7 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.devices;
+import android.location.Location;
import android.net.Uri;
import java.util.ArrayList;
@@ -130,4 +131,6 @@ public interface EventHandler {
void onSetLedColor(int color);
void onPowerOff();
+
+ void onSetGpsLocation(Location location);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java
index 42575cf63..cc517c788 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java
@@ -270,7 +270,7 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator {
public static int getHeartRateMeasurementInterval(String deviceAddress) {
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress));
- return GBApplication.getPrefs().getInt(DeviceSettingsPreferenceConst.PREF_HEARTRATE_MEASUREMENT_INTERVAL, 0) / 60;
+ return prefs.getInt(DeviceSettingsPreferenceConst.PREF_HEARTRATE_MEASUREMENT_INTERVAL, 0) / 60;
}
public static boolean getHeartrateActivityMonitoring(String deviceAddress) throws IllegalArgumentException {
@@ -363,6 +363,18 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator {
return prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_LIFT_WRIST, false);
}
+ public static boolean getWorkoutStartOnPhone(String deviceAddress) {
+ SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress);
+
+ return prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_WORKOUT_START_ON_PHONE, false);
+ }
+
+ public static boolean getWorkoutSendGpsToBand(String deviceAddress) {
+ SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress);
+
+ return prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_WORKOUT_SEND_GPS_TO_BAND, false);
+ }
+
@Override
public boolean supportsScreenshots() {
return false;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java
index d2a07bb41..9742aee5b 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java
@@ -35,8 +35,9 @@ public class HuamiService {
public static final UUID UUID_CHARACTERISTIC_FIRMWARE_DATA = UUID.fromString("00001532-0000-3512-2118-0009af100700");
public static final UUID UUID_UNKNOWN_CHARACTERISTIC0 = UUID.fromString("00000000-0000-3512-2118-0009af100700");
- public static final UUID UUID_UNKNOWN_CHARACTERISTIC1 = UUID.fromString("00000001-0000-3512-2118-0009af100700");
- public static final UUID UUID_UNKNOWN_CHARACTERISTIC2 = UUID.fromString("00000002-0000-3512-2118-0009af100700");
+ public static final UUID UUID_UNKNOWN_RAW_SENSOR_CONTROL = UUID.fromString("00000001-0000-3512-2118-0009af100700");
+ public static final UUID UUID_UNKNOWN_RAW_SENSOR_DATA = UUID.fromString("00000002-0000-3512-2118-0009af100700");
+
/**
* Alarms, Display and other configuration.
*/
@@ -48,9 +49,11 @@ public class HuamiService {
public static final UUID UUID_CHARACTERISTIC_8_USER_SETTINGS = UUID.fromString("00000008-0000-3512-2118-0009af100700");
// service uuid fee1
public static final UUID UUID_CHARACTERISTIC_AUTH = UUID.fromString("00000009-0000-3512-2118-0009af100700");
+ public static final UUID UUID_CHARACTERISTIC_WORKOUT = UUID.fromString("0000000f-0000-3512-2118-0009af100700");
public static final UUID UUID_CHARACTERISTIC_DEVICEEVENT = UUID.fromString("00000010-0000-3512-2118-0009af100700");
public static final UUID UUID_CHARACTERISTIC_AUDIO = UUID.fromString("00000012-0000-3512-2118-0009af100700");
public static final UUID UUID_CHARACTERISTIC_AUDIODATA = UUID.fromString("00000013-0000-3512-2118-0009af100700");
+ public static final UUID UUID_UNKNOWN_CHARACTERISTIC5 = UUID.fromString("00000014-0000-3512-2118-0009af100700");
public static final UUID UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_WRITE = UUID.fromString("00000016-0000-3512-2118-0009af100700");
public static final UUID UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ = UUID.fromString("00000017-0000-3512-2118-0009af100700");
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitband5/AmazfitBand5Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitband5/AmazfitBand5Coordinator.java
index 763c166e7..227b15b12 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitband5/AmazfitBand5Coordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitband5/AmazfitBand5Coordinator.java
@@ -110,6 +110,8 @@ public class AmazfitBand5Coordinator extends HuamiCoordinator {
R.xml.devicesettings_nightmode,
R.xml.devicesettings_liftwrist_display_sensitivity,
R.xml.devicesettings_inactivity_dnd,
+ R.xml.devicesettings_workout_start_on_phone,
+ R.xml.devicesettings_workout_send_gps_to_band,
R.xml.devicesettings_swipeunlock,
R.xml.devicesettings_sync_calendar,
R.xml.devicesettings_reserve_reminders_calendar,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java
index 0c40d3489..5eff096c1 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java
@@ -115,6 +115,8 @@ public class MiBand5Coordinator extends HuamiCoordinator {
R.xml.devicesettings_nightmode,
R.xml.devicesettings_liftwrist_display_sensitivity,
R.xml.devicesettings_inactivity_dnd,
+ R.xml.devicesettings_workout_start_on_phone,
+ R.xml.devicesettings_workout_send_gps_to_band,
R.xml.devicesettings_swipeunlock,
R.xml.devicesettings_sync_calendar,
R.xml.devicesettings_reserve_reminders_calendar,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilFileReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilFileReader.java
index 5e0d74fea..301df6982 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilFileReader.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilFileReader.java
@@ -83,7 +83,7 @@ public class FossilFileReader {
short handle = buf.getShort();
short version = buf.getShort();
- if ((handle == 5630) && (version == 3)) {
+ if ((handle == 5630) && (version == 3 || version == 515 || version == 771)) {
// This is a watch app or watch face
isValid = true;
isApp = true;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/AbstractLocationProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/AbstractLocationProvider.java
new file mode 100644
index 000000000..a361dfca0
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/AbstractLocationProvider.java
@@ -0,0 +1,49 @@
+/* Copyright (C) 2022 José Rebelo
+
+ 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.gps;
+
+import android.content.Context;
+import android.location.LocationListener;
+
+/**
+ * An abstract location provider, which periodically sends a location update to the provided {@link LocationListener}.
+ */
+public abstract class AbstractLocationProvider {
+ private final LocationListener locationListener;
+
+ public AbstractLocationProvider(final LocationListener locationListener) {
+ this.locationListener = locationListener;
+ }
+
+ protected final LocationListener getLocationListener() {
+ return this.locationListener;
+ }
+
+ /**
+ * Start sending periodic location updates.
+ *
+ * @param context the {@link Context}.
+ */
+ abstract void start(final Context context);
+
+ /**
+ * Stop sending periodic location updates.
+ *
+ * @param context the {@link Context}.
+ */
+ abstract void stop(final Context context);
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationListener.java
new file mode 100644
index 000000000..edd9c61ab
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationListener.java
@@ -0,0 +1,80 @@
+/* Copyright (C) 2022 José Rebelo
+
+ 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.gps;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.CurrentPosition;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+/**
+ * An implementation of a {@link LocationListener} that forwards the location updates to the
+ * provided {@link EventHandler}.
+ */
+public class GBLocationListener implements LocationListener {
+ private static final Logger LOG = LoggerFactory.getLogger(GBLocationListener.class);
+
+ private final EventHandler eventHandler;
+
+ private Location previousLocation;
+
+ public GBLocationListener(final EventHandler eventHandler) {
+ this.eventHandler = eventHandler;
+ }
+
+ @Override
+ public void onLocationChanged(final Location location) {
+ LOG.info("Location changed: {}", location);
+
+ // The location usually doesn't contain speed, compute it from the previous location
+ if (previousLocation != null && !location.hasSpeed()) {
+ long timeInterval = (location.getTime() - previousLocation.getTime()) / 1000L;
+ float distanceInMeters = previousLocation.distanceTo(location);
+ location.setSpeed(distanceInMeters / timeInterval);
+ }
+
+ previousLocation = location;
+
+ eventHandler.onSetGpsLocation(location);
+ }
+
+ @Override
+ public void onProviderDisabled(final String provider) {
+ LOG.info("onProviderDisabled: {}", provider);
+ }
+
+ @Override
+ public void onProviderEnabled(final String provider) {
+ LOG.info("onProviderDisabled: {}", provider);
+ }
+
+ @Override
+ public void onStatusChanged(final String provider, final int status, final Bundle extras) {
+ LOG.info("onStatusChanged: {}", provider, status);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationManager.java
new file mode 100644
index 000000000..c657c0017
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationManager.java
@@ -0,0 +1,86 @@
+/* Copyright (C) 2022 José Rebelo
+
+ 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.gps;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Looper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+/**
+ * A static location manager, which keeps track of what providers are currently running. A notification is kept
+ * while there is at least one provider runnin.
+ */
+public class GBLocationManager {
+ private static final Logger LOG = LoggerFactory.getLogger(GBLocationManager.class);
+
+ /**
+ * The current number of running listeners.
+ */
+ private static Map providers = new HashMap<>();
+
+ public static void start(final Context context, final EventHandler eventHandler) {
+ if (providers.containsKey(eventHandler)) {
+ LOG.warn("EventHandler already registered");
+ return;
+ }
+
+ GB.createGpsNotification(context, providers.size() + 1);
+
+ final GBLocationListener locationListener = new GBLocationListener(eventHandler);
+ final AbstractLocationProvider locationProvider = new PhoneGpsLocationProvider(locationListener);
+
+ locationProvider.start(context);
+
+ providers.put(eventHandler, locationProvider);
+ }
+
+ public static void stop(final Context context, final EventHandler eventHandler) {
+ final AbstractLocationProvider locationProvider = providers.remove(eventHandler);
+
+ if (locationProvider != null) {
+ LOG.warn("EventHandler not registered");
+
+ locationProvider.stop(context);
+ }
+
+ if (!providers.isEmpty()) {
+ GB.createGpsNotification(context, providers.size());
+ } else {
+ GB.removeGpsNotification(context);
+ }
+ }
+
+ public static void stopAll(final Context context) {
+ for (EventHandler eventHandler : providers.keySet()) {
+ stop(context, eventHandler);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/MockLocationProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/MockLocationProvider.java
new file mode 100644
index 000000000..60f3f75bf
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/MockLocationProvider.java
@@ -0,0 +1,96 @@
+/* Copyright (C) 2022 José Rebelo
+
+ 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.gps;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.os.Handler;
+import android.os.Looper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.CurrentPosition;
+
+/**
+ * A mock location provider which keeps updating the location at a constant speed, starting from the
+ * last known location. Useful for local tests.
+ */
+public class MockLocationProvider extends AbstractLocationProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(MockLocationProvider.class);
+
+ private Location previousLocation = new CurrentPosition().getLastKnownLocation();
+
+ /**
+ * Interval between location updates, in milliseconds.
+ */
+ private final int interval = 1000;
+
+ /**
+ * Difference between location updates, in degrees.
+ */
+ private final float coordDiff = 0.0002f;
+
+ /**
+ * Whether the handler is running.
+ */
+ private boolean running = false;
+
+ private final Handler handler = new Handler(Looper.getMainLooper());
+
+ private final Runnable locationUpdateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!running) {
+ return;
+ }
+
+ final Location newLocation = new Location(previousLocation);
+ newLocation.setLatitude(previousLocation.getLatitude() + coordDiff);
+ newLocation.setTime(System.currentTimeMillis());
+
+ getLocationListener().onLocationChanged(newLocation);
+
+ previousLocation = newLocation;
+
+ if (running) {
+ handler.postDelayed(this, interval);
+ }
+ }
+ };
+
+ public MockLocationProvider(LocationListener locationListener) {
+ super(locationListener);
+ }
+
+ @Override
+ void start(final Context context) {
+ LOG.info("Starting mock location provider");
+
+ running = true;
+ handler.postDelayed(locationUpdateRunnable, interval);
+ }
+
+ @Override
+ void stop(final Context context) {
+ LOG.info("Stopping mock location provider");
+
+ running = false;
+ handler.removeCallbacksAndMessages(null);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/PhoneGpsLocationProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/PhoneGpsLocationProvider.java
new file mode 100644
index 000000000..88880a4eb
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/PhoneGpsLocationProvider.java
@@ -0,0 +1,66 @@
+/* Copyright (C) 2022 José Rebelo
+
+ 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.gps;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Looper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A location provider that uses the phone GPS, using {@link LocationManager}.
+ */
+public class PhoneGpsLocationProvider extends AbstractLocationProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(PhoneGpsLocationProvider.class);
+
+ private static final int INTERVAL_MIN_TIME = 1000;
+ private static final int INTERVAL_MIN_DISTANCE = 0;
+
+ public PhoneGpsLocationProvider(LocationListener locationListener) {
+ super(locationListener);
+ }
+
+ @Override
+ void start(final Context context) {
+ LOG.info("Starting phone gps location provider");
+
+ final LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ locationManager.removeUpdates(getLocationListener());
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ INTERVAL_MIN_TIME,
+ INTERVAL_MIN_DISTANCE,
+ getLocationListener(),
+ Looper.getMainLooper()
+ );
+
+ final Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ LOG.debug("Last known location: {}", lastKnownLocation);
+ }
+
+ @Override
+ void stop(final Context context) {
+ LOG.info("Stopping phone gps location provider");
+
+ final LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ locationManager.removeUpdates(getLocationListener());
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/opentracks/OpenTracksContentObserver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/opentracks/OpenTracksContentObserver.java
new file mode 100644
index 000000000..cd2374338
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/opentracks/OpenTracksContentObserver.java
@@ -0,0 +1,100 @@
+/* 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.opentracks;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+
+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 Listsec/mJóga
- Skákací lano
+ ŠvihadloEliptický trenažérSálová CyklistikaPlavání (otevřená voda)
@@ -1641,4 +1641,77 @@
Konfigurace hodin pro jiná časová pásmaOdstranit \'%1$s\'Žádné volné místo
+ Sony WF-1000XM3
+ Galaxy Buds Pro
+ Okolní zvuk
+ Povolit přístup k internetu
+ Povolit všem aplikacím na hodinkách přístup k internetu
+ Povolit Intenty
+ 105 tepů/min
+ 100 tepů/min
+ 110 tepů/min
+ 112 tepů/min
+ Ovládání přepínání Vpravo
+ Hlasitost
+ Spotify
+ Přepínání regulace hluku
+ %1$s potřebuje přístup k Notifikacím, aby bylo možno notifikace zobrazovat i na připojených hodinkách/náramku.
+\n
+\nZvolte \'%2$s\', poté \'%1$s\' a a vyberte \'Povolit přístup k Notifikacím\', poté zvolte \'Zpět\' pro návrat do %1$s
+ %1$s potřebuje přístup k funkci Nerušit, aby bylo možno nastavit Nerušit i na připojených hodinkách/náramku.
+\n
+\nZvolte \'%2$s\', poté \'%1$s\' a a vyberte \'Povolit přístup funkci Nerušit\', poté zvolte \'Zpět\' pro návrat do %1$s
+ Přehrávání hovorů do sluchátek, jsou li v uších
+ 130 tepů/min
+ Text jako Bitmapa
+ Pokud není možno zobrazit dané slovo pomocí fontu hodinek, Gadgetbridge vykreslí obrázek, který se na hodinkách zobrazí
+ 135 tepů/min
+ 150 tepů/min
+ Sledování stresu
+ Automaticky povolit okolní zvuk a snížit přehrávání po zjištění hlasu
+ Umožní aplikacím Bangle.js hodinek posílat Android Intenty a povolí ostatním Android aplikacím (Tasker) aby posílaly data do Bangle.js pomocí com.banglejs.uart.tx Intentu.
+ Zavibruje řemínkem, pokud tepová frekvence překročí prahovou hodnotu bez zjevné fyzické aktivity v posledních 10 minutách. Tato funkce je experimentální a nebyla důkladně testována.
+ Automaticky zvýší četnost detekce srdeční frekvence při fyzické aktivitě.
+ Monitorování srdeční frekvence
+ Okolní zvuk během hovoru
+ Ovládání přepínání Vlevo
+ Okolní zvuk ←→ Vypnuto
+ 120 tepů/min
+ 140 tepů/min
+ 125 tepů/min
+ Upozornění na srdeční frekvenci (experimentální)
+ Sledování úrovně stresu při odpočinku
+ 145 tepů/min
+ Prahová hodnota upozornění na srdeční frekvenci
+ Sledování aktivity
+ Konfigurace monitorování srdečního tepu
+ Konfigurace monitorování srdečního tepu a prahových hodnot výstrah
+ Zjednodušené přepínání připojení
+ Přepíná sluchátka automaticky mezi připojenými zařízeními
+ Okolní hlasitost Vlevo
+ Okolní hlasitost Vpravo
+ Přizpůsobení nastavení okolního zvuku
+ Během hovoru slyšet vlastní hlas
+ Možnosti okolního zvuku
+ Úroveň aktivního potlačení hluku
+ Vysoká
+ Aktivní potlačení hluku
+ Potlačení hluku ←→ Vypnuto
+ Konec po klidu za:
+ Nízká
+ Rychlý okolní zvuk
+ Regulace hluku s jedním sluchátkem
+ Od Měkkého po Jasný
+ Vyrovnání zvuku
+ Potlačení hluku ←→ Okolní zvuk
+ Regulace hluku
+ Detekce hlasu
+ Hlasový asistent
+ Povolit regulaci hluku i při použití pouze s jednoho sluchátka
+ Tón okolního zvuku
+ Dvojité klepnutí na okraj
+ Detekce dvojitého klepnutí, i když není klepnuto na dotykový panel
+ 5 sekund
+ 15 sekund
+ 10 sekund
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 0da11a744..06f9c1833 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1627,4 +1627,5 @@
Bangle.js läuftÜber Bangle.js GadgetbridgeFitness-App-Tracking umschalten
+ Text als Bitmaps
\ 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 820f5adff..bf3db0403 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -474,4 +474,11 @@
VESCLeveys:Aikavyöhyke:
+ automaattinen
+ tunnista soutaminen
+ tunnista juokseminen
+ Valikko
+ tunnista käveleminen
+ kysy
+ tunnista pyöräily
\ 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 4863b5340..bf4735689 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -1699,4 +1699,10 @@
צליל הקפי במהלך שיחהלזהות נגיעה כפולה כשבוצעה בקצוות משטח המגעלהפעיל את הרטט בצמיד כאשר הדופק יורד מתחת לסף, ללא פעילות פיזית מובהקת ב־10 הדקות האחרונות. יכולת זאת נמצאת בשלבי ניסוי ולא נבדקה באופן קפדני.
+ טקסט כמפת סיביות
+ לאפשר גישה לאינטרנט
+ לאפשר ליישומונים במכשיר הזה לגשת לאינטרנט
+ אם אי אפשר לעבד תמונות עם גופן השעון, הוא יומר למפת סיביות ב־Gadgetbridge שתוצג בשעון
+ לאפשר ליישומוני שעון של Bangle.js לשלוח Intents ל־Android ולאפשר ליישומי Android אחרים (כמו Tasker) לשלוח נתונים ל־Bangle.js באמצעות ה־Intent com.banglejs.uart.tx.
+ לאפשר Intents
\ 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 3c6de9d58..62ae79793 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -9,12 +9,12 @@
SynchroniseerZoek verloren apparaatScreenshot maken
- Ontkoppel
+ Verbinding verbrekenVerwijder apparaatVerwijder %1$sDit zal het apparaat en alle bijbehorende gegevens verwijderen!
- Druk lang op de kaart om te ontkoppelen
- Ontkoppelen
+ Druk lang op de kaart om de verbinding te verbreken
+ Verbinding verbrekenVerbinden…Een screenshot maken van het apparaatDebug
@@ -1698,4 +1698,12 @@
150 bpm135 bpm140 bpm
+ Apps op dit apparaat toegang geven tot internet
+ Sta toe dat Bangle.js apps Android-intents verzenden en sta andere apps op Android (zoals Tasker) toe om gegevens naar Bangle.js te verzenden met de com.banglejs.uart.tx-intent.
+ Tekst als afbeelding
+ Internettoegang toestaan
+ Als een woord niet kan worden weergegeven met het lettertype van het horloge, render het dan naar een afbeelding in Gadgetbridge en toon de afbeelding op het horloge
+ Intents toestaan
+ Bediening links
+ Bediening rechts
\ 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 28eea76cc..5290f29e2 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -1606,4 +1606,39 @@
Lokalizacja nie jest dostępna. Najprawdopodobniej jest to spowodowane nowym systemem uprawnień Androida. Prawdopodobnie automatyczny eksport teraz nie działa.Podświetlanie nowych powiadomieńE-mail
+ Znajdź urządzenie
+ Przypomnienie o wydarzeniach
+ Alerty o bezczynności
+ Wzory wibracji
+ Skonfiguruj wzory wibracji dla różnych powiadomień
+ Sony WF-1000XM3
+ Galaxy Buds Pro
+ O Bangle.js Gadgetbridge
+ Bangle.js Gadgetbridge
+ Bangle.js Gadgetbridge
+ Bangle.js jest uruchomiony
+ O Bangle.js Gadgetbridge
+ Bangle.js Gadgetbridge
+ Bangle.js Gadgetbridge
+ Bangle.js jest uruchomiony
+ Tekst jako bitmapy
+ Zezwól na dostęp do internetu
+ Zezwól aplikacjom na tym urządzeniu na dostęp do Internetu
+ 100 bpm
+ Usuń \'%1$s\'
+ Strefa czasowa
+ 105 bpm
+ 110 bpm
+ 112 bpm
+ 120 bpm
+ 125 bpm
+ 130 bpm
+ 135 bpm
+ 140 bpm
+ 145 bpm
+ 150 bpm
+ Spotify
+ 5 sekund
+ 10 sekund
+ 15 sekund
\ 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 b7e170fd9..98c6e557f 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1716,4 +1716,10 @@
Kalp ritmi izlemeyi ve uyarı eşiklerini yapılandırınKulaklıkları eşleştirilen aygıtlar arasında otomatik olarak değiştirirOrtam Ses Seviyesi Sağ
+ Bit Eşlem Olarak Metin
+ Bangle.js saat uygulamalarının Android Amaçları göndermesine ve Android\'deki diğer uygulamaların (Tasker gibi) com.banglejs.uart.tx Niyeti ile Bangle.js\'ye veri göndermesine izin ver.
+ Amaçlara İzin Ver
+ Bu aygıttaki uygulamaların internete erişmesine izin ver
+ Bir sözcük saatin yazı tipi kullanılarak görüntülenemiyorsa, onu Gadgetbridge\'de bir bit eşleme dönüştür ve bit eşlemi saatte görüntüle
+ İnternet Erişimine İzin Ver
\ 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 03c8d82c6..fe6f64609 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -1707,4 +1707,10 @@
Налаштуйте пороги стеження за частотою серцевих скорочень та попередженьСтеження за частотою серцевих скороченьНалаштувати стеження за частотою серцевих скорочень
+ Дозволити доступ до інтернету
+ Дозволити застосункам на цьому пристрої доступ до інтернету
+ Дозволити застосункам для годинника Bangle.js надсилати наміри Android і дозволити іншим застосункам на Android (наприклад, Tasker) надсилати дані до Bangle.js із наміром com.banglejs.uart.tx.
+ Дозволити наміри
+ Текст у вигляді растрових зображень
+ Якщо слово не може бути відтворено шрифтом годинника, перетворювати його на растрове зображення в Gadgetbridge і показувати растрове зображення на годиннику
\ 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 c41056920..c6fc36a9f 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -1705,4 +1705,10 @@
心率监测配置心率监测配置心率监测和警报阈值
+ 作为位图的文本
+ 如果无法使用手表的字体呈现单词,请将其呈现为 Gadgetbridge 中的位图,并在手表上显示位图
+ 允许 Bangle.js 手表应用发送 Android 意图,并允许 Android 上的其他应用(如 Tasker)使用 com.banglejs.uart.tx 意图向 Bangle.js 发送数据。
+ 允许意向
+ 允许互联网访问
+ 允许此设备上的应用访问互联网
\ 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 f9d8d1377..325e74cbd 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -462,6 +462,7 @@
@string/menuitem_activity@string/menuitem_eventreminder@string/menuitem_pai
+ @string/menuitem_sleep@string/menuitem_worldclock@string/menuitem_stress@string/menuitem_cycles
@@ -489,6 +490,7 @@
@string/p_menuitem_activity@string/p_menuitem_eventreminder@string/p_menuitem_pai
+ @string/p_menuitem_sleep@string/p_menuitem_worldclock@string/p_menuitem_stress@string/p_menuitem_cycles
@@ -516,6 +518,7 @@
@string/p_menuitem_activity@string/p_menuitem_eventreminder@string/p_menuitem_pai
+ @string/p_menuitem_sleep@string/p_menuitem_worldclock@string/p_menuitem_stress@string/p_menuitem_cycles
@@ -540,6 +543,7 @@
@string/menuitem_mutephone@string/menuitem_eventreminder@string/menuitem_pai
+ @string/menuitem_sleep@string/menuitem_worldclock@string/menuitem_stress@string/menuitem_cycles
@@ -565,6 +569,7 @@
@string/p_menuitem_mutephone@string/p_menuitem_eventreminder@string/p_menuitem_pai
+ @string/p_menuitem_sleep@string/p_menuitem_worldclock@string/p_menuitem_stress@string/p_menuitem_cycles
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f2b95c3b6..3ab499e12 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -372,6 +372,10 @@
Heart rate alarm during sports activityLow limitHigh limit
+ Fitness app tracking
+ Start/stop fitness app tracking on phone when a GPS workout is started on the band
+ Send GPS during workout
+ Send the current GPS location to the band during a workoutAuto exportAuto export enabled
@@ -1031,6 +1035,9 @@
High-priorityData transferLow battery
+ GPS tracking
+ Gadgetbridge GPS
+ Sending GPS location to %1$d devicesAmazfit GTSAmazfit Verge LiteLemfo SG2
@@ -1627,6 +1634,7 @@
Fitness App Tracking StartFitness App Tracking StopToggle Fitness App Tracking
+ GPS Location Listener Stop###m
diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml
index bba56bd89..ecfaeaa34 100644
--- a/app/src/main/res/xml/changelog_master.xml
+++ b/app/src/main/res/xml/changelog_master.xml
@@ -1,5 +1,11 @@
+
+ Huami: Fix long music track names not displaying
+ Amazfit Bip U/Pro/Band 5: Enable extended HR/stress monitoring setting
+ Pebble: Fix calendar blacklist, view and storage
+ FitPro: Fix crash, inactivity warning preference to string
+ Initial Support for Sony WF-1000XM3Initial Support for Galaxy Buds Pro
diff --git a/app/src/main/res/xml/devicesettings_inactivity_extended.xml b/app/src/main/res/xml/devicesettings_inactivity_extended.xml
index 7063b8572..766e865ab 100644
--- a/app/src/main/res/xml/devicesettings_inactivity_extended.xml
+++ b/app/src/main/res/xml/devicesettings_inactivity_extended.xml
@@ -21,7 +21,7 @@
android:dependency="inactivity_warnings_enable"
android:entries="@array/inactivity_minutes"
android:entryValues="@array/inactivity_minutes_values"
- android:key="inactivity_warnings_threshold"
+ android:key="inactivity_warnings_threshold_extended"
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings_threshold" />
diff --git a/app/src/main/res/xml/devicesettings_workout_send_gps_to_band.xml b/app/src/main/res/xml/devicesettings_workout_send_gps_to_band.xml
new file mode 100644
index 000000000..ed82a58f0
--- /dev/null
+++ b/app/src/main/res/xml/devicesettings_workout_send_gps_to_band.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/xml/devicesettings_workout_start_on_phone.xml b/app/src/main/res/xml/devicesettings_workout_start_on_phone.xml
new file mode 100644
index 000000000..fb9054d8f
--- /dev/null
+++ b/app/src/main/res/xml/devicesettings_workout_start_on_phone.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/xml/sleep_alarm_widget_info.xml b/app/src/main/res/xml/sleep_alarm_widget_info.xml
index c65c4ca38..dcf5c6ab1 100644
--- a/app/src/main/res/xml/sleep_alarm_widget_info.xml
+++ b/app/src/main/res/xml/sleep_alarm_widget_info.xml
@@ -6,4 +6,6 @@
android:minWidth="40dp"
android:previewImage="@drawable/ic_launcher"
android:updatePeriodMillis="86400000"
- android:widgetCategory="home_screen">
\ No newline at end of file
+ android:configure="nodomain.freeyourgadget.gadgetbridge.activities.SleepAlarmWidgetConfigurationActivity"
+ android:widgetCategory="home_screen">
+
\ No newline at end of file
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/CalendarEventTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/CalendarEventTest.java
index bd8c6919e..fcf4bcb93 100644
--- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/CalendarEventTest.java
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/CalendarEventTest.java
@@ -19,12 +19,17 @@ public class CalendarEventTest extends TestBase {
private static final long ID_1 = 100;
private static final long ID_2 = 101;
private static final String CALNAME_1 = "cal1";
+ private static final String CALACCOUNTNAME_1 = "account1";
+ private static final int COLOR_1 = 185489;
@Test
- public void testHashCode() {
- CalendarEvents.CalendarEvent c1 = new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, "something", null, null, CALNAME_1, false);
- CalendarEvents.CalendarEvent c2 = new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, false);
- CalendarEvents.CalendarEvent c3 = new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, null, "something", CALNAME_1, false);
+ public void testHashCode() {
+ CalendarEvents.CalendarEvent c1 =
+ new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, "something", null, null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
+ CalendarEvents.CalendarEvent c2 =
+ new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
+ CalendarEvents.CalendarEvent c3 =
+ new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, null, "something", CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
assertEquals(c1.hashCode(), c1.hashCode());
assertNotEquals(c1.hashCode(), c2.hashCode());
@@ -35,7 +40,7 @@ public class CalendarEventTest extends TestBase {
@Test
public void testSync() {
List eventList = new ArrayList<>();
- eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, false));
+ eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false));
GBDevice dummyGBDevice = createDummyGDevice("00:00:01:00:03");
dummyGBDevice.setState(GBDevice.State.INITIALIZED);
@@ -44,7 +49,7 @@ public class CalendarEventTest extends TestBase {
testCR.syncCalendar(eventList);
- eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_2, null, "something", null, CALNAME_1, false));
+ eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_2, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false));
testCR.syncCalendar(eventList);
CalendarSyncStateDao calendarSyncStateDao = daoSession.getCalendarSyncStateDao();
diff --git a/external/fossil-hr-watchface b/external/fossil-hr-watchface
index aad2a141c..f07ed376e 160000
--- a/external/fossil-hr-watchface
+++ b/external/fossil-hr-watchface
@@ -1 +1 @@
-Subproject commit aad2a141cb2e151431f8354e52d9b74f6829855a
+Subproject commit f07ed376e9046dbcc9c5d7821117c80b2d79ffd1
diff --git a/fastlane/metadata/android/en-US/changelogs/212.txt b/fastlane/metadata/android/en-US/changelogs/212.txt
new file mode 100644
index 000000000..7921bd751
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/212.txt
@@ -0,0 +1,4 @@
+* Huami: Fix long music track names not displaying
+* Amazfit Bip U/Pro/Band 5: Enable extended HR/stress monitoring setting
+* Pebble: Fix calendar blacklist, view and storage
+* FitPro: Fix crash, inactivity warning preference to string